来自 C#/.NET 服务的 Google Play Android Developer API - (400) 错误请求

Posted

技术标签:

【中文标题】来自 C#/.NET 服务的 Google Play Android Developer API - (400) 错误请求【英文标题】:Google Play Android Developer API from C#/.NET service - (400) Bad Request 【发布时间】:2013-07-25 12:37:09 【问题描述】:

我正在尝试使用 Google API .NET 客户端库从我的 ASP.NET Web 服务器访问 Purchase Status API,这是使用 Purchase API v1.1 的推荐方式。但是,此 API 的 Authorization page 建议将 Web 请求直接发送到 Google 的 OAuth2 页面,而不是使用相应的客户端库。

好的,我用我能想象到的所有变体尝试了这两种方法,它们都导致“远程服务器返回错误:(400) 错误请求。”。

现在我做了什么来表达我的观点。首先,我已经完成了授权页面的创建 API 控制台项目下的所有步骤 1-8。接下来,我按照那里的描述生成了一个刷新令牌。在刷新令牌生成期间,我选择了与发布我的 android 应用程序(现在处于发布测试版状态)相同的 Google 帐户。

接下来我创建了一个控制台 C# 应用程序,用于在 Visual Studio 中进行测试(可能是控制台应用程序有问题?) 并尝试使用此代码(在一些 Google API 示例中找到)调用购买 API:

    private static void Main(string[] args)
    
        var provider =
            new WebServerClient(GoogleAuthenticationServer.Description)
                
                    ClientIdentifier = "91....751.apps.googleusercontent.com",
                    ClientSecret = "wRT0Kf_b....ow"
                ;
        var auth = new OAuth2Authenticator<WebServerClient>(
            provider, GetAuthorization);

        var service = new AndroidPublisherService(
            new BaseClientService.Initializer()
                
                    Authenticator = auth,
                    ApplicationName = APP_NAME
                );

        var request = service.Inapppurchases.Get(
            PACKAGE_NAME, PRODUCT_ID, PURCHASE_TOKEN);
        var purchaseState = request.Execute();

        Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
    

    private static IAuthorizationState GetAuthorization(WebServerClient client)
    
        IAuthorizationState state =
            new AuthorizationState(
                new[] "https://www.googleapis.com/auth/androidpublisher")
                
                    RefreshToken = "4/lWX1B3nU0_Ya....gAI"
                ;

        // below is my redirect URI which I used to get a refresh token
        // I tried with and without this statement
        state.Callback = new Uri("https://XXXXX.com/oauth2callback/");

        client.RefreshToken(state); // <-- Here we have (400) Bad request
        return state;
    

然后我尝试了这段代码来获取访问令牌(我在这里找到它:Google Calendar API - Bad Request (400) Trying To Swap Code For Access Token):

    public static string GetAccessToken()
    
        var request = WebRequest.Create(
            "https://accounts.google.com/o/oauth2/token");
        request.Method = "POST";
        var postData =
            string.Format(
                @"code=0&client_id=1&client_secret=2&redirect_uri=3&grant_type=authorization_code",
            // refresh token I got from browser
            // also tried with Url encoded value
            // 4%2FlWX1B3nU0_Yax....gAI
                "4/lWX1B3nU0_Yax....gAI",
            // ClientID from Google APIs Console
                "919....1.apps.googleusercontent.com",
            // Client secret from Google APIs Console
                "wRT0Kf_bE....w",
            // redirect URI from Google APIs Console
            // also tried Url encoded value
            // https%3A%2F%2FXXXXX.com%2Foauth2callback%2F
                "https://XXXXX.com/oauth2callback/");

        byte[] byteArray = Encoding.UTF8.GetBytes(postData);
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = byteArray.Length;
        using (var dataStream = request.GetRequestStream())
        
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
        
        try
        
            // request.GetResponse() --> (400) Bad request again!
            using (var response = request.GetResponse())
            
                using (var dataStream = response.GetResponseStream())
                
                    using (var reader = new StreamReader(dataStream))
                    
                        var responseFromServer = reader.ReadToEnd();
                        var jsonResponse = JsonConvert.DeserializeObject<OAuth2Response>(responseFromServer);
                        return jsonResponse.access_token;
                    
                
            
        
        catch (Exception ex)  var x = ex; 
        return null;
    

所以,总结我所有的长篇故事:

    是否可以使用上述任一方法从 C# 控制台应用程序(无需用户交互)通过 OAuth2 授权? 我已经仔细检查了重定向 URI(因为我在 *** 上看到了很多关于它的讨论问题)和其他参数,如 ClientID 和 ClientSecret。我在上面的代码中还可以做错什么? 我是否需要在刷新令牌中对斜线进行 URL 编码(我看到使用客户端库的第一种方法可以做到)? 实现我的最终目标(从 ASP.NET Web 服务器购买 API 访问)的推荐方法是什么?

【问题讨论】:

你为什么要传递刷新令牌?我们所有的示例都使用常规的 OAuth2 流来获取访问令牌和刷新令牌。查看我们的示例存储库(您可以查看驱动示例 - code.google.com/p/google-api-dotnet-client/source/browse/…)。在这种情况下,.NET 客户端库将为您设置每个请求的授权标头(在库与访问令牌交换代码之后)。请尝试使用我们所有示例中提到的 OAuth2 库。 @peleyal 这些例子是我的出发点。但是,Google Drive 示例使用 AuthorizationMgr 类,它在后台使用 WindowTitleNativeAuthorizationFlowLoopbackServerAuthorizationFlow 类来执行 OAuth 任务。它们似乎都不适合服务,因为WindowTitleNativeAuthorizationFlow 调用OAuth2AuthorizationDialog.ShowDialog(url)LoopbackServerAuthorizationFlow 调用Process.Start(authUrl.ToString())。我需要在 ASP.NET 进程的上下文中执行 OAuth 授权没有用户交互。 【参考方案1】:

我会尽力回答你的最后一个问题。如果您访问自己的数据帐户,则不需要在 oAuth2 中使用客户端 ID。让我们使用服务帐户来访问 Google Play API。

    Google Developer Console > Your project > APIs and auth > Credentials > Create a new key 中创建服务帐户。您将下载 p12 密钥。 创建一个 C# 项目。您可以选择控制台应用程序。 从Google.Apis.androidpublisher. Nuget 安装google play api 库。您可以在Google APIs Client Library for .NET 中找到其他 dotnet 库

    在 API 访问中将 google api 项目与您的 google play 帐户关联

    验证并尝试查询信息。我会尝试列出所有应用内项目。您可以更改以获取购买状态

    String serviceAccountEmail = "your-mail-in-developer-console@developer.gserviceaccount.com";
    
        var certificate = new X509Certificate2(@"physical-path-to-your-key\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
    
        ServiceAccountCredential credential = new ServiceAccountCredential(
           new ServiceAccountCredential.Initializer(serviceAccountEmail)
           
               Scopes = new[]  "https://www.googleapis.com/auth/androidpublisher" 
           .FromCertificate(certificate));
    
    
        var service = new AndroidPublisherService(
       new BaseClientService.Initializer()
       
           HttpClientInitializer = credential,
           ApplicationName = "GooglePlay API Sample",
       );
    // try catch this function because if you input wrong params ( wrong token) google will return error.
        var request = service.Inappproducts.List("your-package-name");
        var purchaseState = request.Execute();
    
       // var request = service.Purchases.Products.Get(
       //"your-package-name", "your-inapp-item-id", "purchase-token"); get purchase'status
    
    
    
        Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
    

【讨论】:

在有关 Play API 访问的普遍困惑及其许多悬而未决的问题中,您出色的答案提供了一个可行的分步指南和一段简单的代码。谢谢! 你真的解决了问题!非常感谢您分享您的解决方案。很多人一直在围绕这个主题创造熵,但你建议的那个是迄今为止我找到的最好的解决方案。 这个答案 + 这个:***.com/a/41454381/2293226 帮助解决了我的问题 这行得通,但我只收到 400 Invalid Value 作为我所有令牌的响应。 甚至可以使用 JSON 密钥代替 P12 证书。 var credential = GoogleCredential.FromFile(@"api-1234.json").CreateScoped(AndroidPublisherService.Scope.Androidpublisher);【参考方案2】:

你应该在你的 私有静态 IAuthorizationState GetAuthorization(WebServerClient 客户端) 方法:

        private IAuthorizationState GetAuthorization(WebServerClient client)
        
            IAuthorizationState state = AuthState;
            if (state != null)
            
                return state;
            

            state = new AuthorizationState()
            
                RefreshToken = "4/lWX1B3nU0_Ya....gAI",
                Callback = new Uri(@"https://XXXXX.com/oauth2callback/")
            ;
            client.RefreshToken(state);

            // Store and return the credentials.
            HttpContext.Current.Session["AUTH_STATE"] = _state = state;
            return state;
        
    让我知道它是否适合您。 请注意,我们知道今天整个 OAuth2 流程很尴尬,我们正在努力改进它。

【讨论】:

如果第一条语句中的 AuthState 是读取先前保存的 IAuthorizationState 的内容,那么我认为您的代码和我的代码之间没有实质性差异,除非您省略了 AuthorizationState 构造函数的范围并指定了 @987654325 @ 在类初始值设定项中,而不是使用属性。我已经使用 NuGet 包管理器更新了所有客户端库,不幸的是结果相同 - 400。如果您是 Google 开发团队的一员,我可以将我的实际值发送给您,甚至向您发送完整的 Visual Studio 解决方案以检查它. 400 响应是针对您的刷新令牌请求还是获取您的购买?你能附上请求的提琴手输出吗? (没有您的实际 client_id、secrets 和 refresh_token 值 是的,我收到了对我的刷新令牌请求的响应 "error" : "invalid_grant" (400)。 Fiddler 显示请求如下: POST accounts.google.com/o/oauth2/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded; charset=utf-8 用户代理:DotNetOpenAuth/4.0.0.11165 主机:accounts.google.com 缓存控制:无存储,无缓存 编译指示:无缓存 内容长度:191 预期:100-继续 连接:保持-Alive refresh_token=4%2FlWX1B3...AI&grant_type=refresh_token&client_id=9...1.apps.googleusercontent.com&client_secret=wRT...Yow 我附加的代码(看起来和你的很相似,对我有用)。我会继续调查这个。你有没有像我一样尝试构建没有作用域的 AuthorizationState?

以上是关于来自 C#/.NET 服务的 Google Play Android Developer API - (400) 错误请求的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 .net 客户端 sdk 使用来自 Google PubSub 模拟器的消息

前传gRPC in ASP.NET Core 3.0 -- Protocol Buffer

apache_conf 来自https://snipt.net/public/ .htaccess HTTP缓存(Google PageSpeed Friendly)

使用 c# .net 库检查来自 gmail 服务器的 IMAP 消息 [关闭]

Google play 游戏服务再次显示来自 google+ 的登录屏幕

来自服务器的 Google 身份验证