GoogleJsonResponseException:400错误的请求-failedPrecondition

时间:2019-12-23 13:33:17

标签: gmail-api

我正在尝试使用Gmail API。我的目的是通过应用程序(以Google条款服务帐户)从我的Gmail帐户发送电子邮件。我已经被以下错误消息困扰了一段时间。 :

com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Bad Request",
    "reason" : "failedPrecondition"
  } ],
  "message" : "Bad Request"
}

在发布此问题之前,我已经搜索过Google和Stackoverflow。我发现我需要 创建组织,因为我只想在内部使用该应用程序。要创建组织,我需要创建自己的域(在.de TLD中)并在Google中注册。 Google还希望在我的这个域中提供一个电子邮件地址,然后由我提供。
然后,我根据一些Stackoverflow答案的建议,在OAuth集中屏幕和Google云管理员安全页面上为Gmail设置了所有可能的范围。我还设置了域范围的委托,这也是建议的做法。总而言之,据我所知,我已按照各种问题和答案的建议采取了所有必要的步骤。
在我的代码中(见下文),我插入了credential.refreshToken();语句,以强制更早地访问OAuth例程。这有助于我解决Gmail范围的早期问题。在这里值得注意的是,对GoogleCredential.fromStream()的调用将创建一个没有范围的凭证。必须进一步调用credential.createScoped()才能初始化通过OAuth获得的范围。
我第一次尝试使用Gmail是尝试发送简单的电子邮件。这不起作用(与上述错误相同)。因此,我决定使用一个更简单的测试-阅读Gmail帐户上的标签。再次,这不适用于上面显示的错误消息。如下面的代码所示,我不确定Gmail API所需的 appName emailId 使用什么值。因此,我对想到的所有可能的值进行了迭代。

我有以下问题:

  • Gmail API是否正在尝试通过在我的域中注册的电子邮件地址(出于讨论目的,ds.deds@ds.de(使用虚构示例)发送电子邮件,而不是我的Gmail帐户?如果是这样,那么我可能正在尝试做一些不可能的事情。
  • 是否可以从Google获取有关 failedPrecondition 是什么的更多错误信息?
  • 在设置服务帐户时是否可能错过了一些东西?从各种Stackoverflow答案中可以看到,我已正确完成了所有操作,但也许我错过了一个很小但至关重要的步骤。

    自发布此问题以来,我发现了以下问题和答案: nodejs gmail failedPrecondition
    在点击回答者 DaImTo 时,我发现他或她是Google Developer Expert,已被Google认可。因此,我认为我可以回答我的问题-我试图做的事情是不可能的!可惜!

测试代码

`
   public static void main(String[] args) {
      testOauthService();
   }

   public static void testOauthService() {
      HttpTransport HTTP_TRANSPORT;
      try {
         HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
      } catch (Exception ex) {
         System.out.println("Exception creating HTTPTransport: " + ex.getMessage());
         return;
      }

      final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

      System.out.println("public static void testOauthService () called. ");

      System.out.println("Credentials file is: " + credentialFile.getAbsolutePath());

      if (!credentialFile.exists()) {
         System.err.println("The credentials file " + credentialFile.getAbsolutePath() + " does not exist!");
         return;
      }

      String[] scopeEnums = { GmailScopes.MAIL_GOOGLE_COM, GmailScopes.GMAIL_COMPOSE, GmailScopes.GMAIL_INSERT,
               GmailScopes.GMAIL_LABELS, GmailScopes.GMAIL_METADATA, GmailScopes.GMAIL_MODIFY,
               GmailScopes.GMAIL_READONLY, GmailScopes.GMAIL_SEND, GmailScopes.GMAIL_SETTINGS_BASIC,
               GmailScopes.GMAIL_SETTINGS_SHARING };

      boolean bFound = false;
      String uid = USER_ID;
      for (String appName : APP_NAMES) {
         try {
            System.out.println("App name: " + appName + ", userId = " + uid);
            FileInputStream credentialStream = new FileInputStream(credentialFile);
            GoogleCredential credential;
            credential = GoogleCredential.fromStream(credentialStream, HTTP_TRANSPORT, JSON_FACTORY);
            credential = credential.createScoped(Arrays.asList(scopeEnums));
            // The following statement is probably not necessary in production. It does however help with diagnosing
            // OAuth problems by forcing an earlier access to OAuth and thus easing later diagnosis.
            credential.refreshToken();
            System.out.println("Token successfully refreshed!");

            Gmail.Builder builder = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential);
            builder.setApplicationName(appName);
            Gmail gmail = builder.build();
            Users users = gmail.users();
            GetProfile getProfile = users.getProfile(uid);
            System.out.println("Profile user id = " + getProfile.getUserId() + ", " + getProfile.toString());

            // Lets try to use some simple Gmail functionality - getting a list of the labels.
            Gmail.Users.Labels labels = users.labels();
            for (String emailId : EMAIL_USER_IDS) {
               System.out.println("Trying labels.list() for email id " + emailId);
               try {
                  Gmail.Users.Labels.List labelList = labels.list(emailId);
                  ListLabelsResponse response = labelList.execute();
                  System.out.println("labelList.response: " + response.toString());
               } catch (Exception ex) {
                  System.out.println("Exception in label listing: " + ex.getClass().getName() + ": " + ex.getMessage());
                  continue;
               }
               System.out.println("Label listing successful with App Name = " + appName + ", User Id = " + uid
                        + ", email id = " + emailId);
               bFound = true;
            }

         } catch (Exception ex) {
            System.out.println("Exception: " + ex.getClass().getName() + ": " + ex.getMessage());
            ex.printStackTrace();
            continue;
         }
         if (bFound)
            break;
      }

      System.out.println("public static void testOauthService () finished");
   }
`

2 个答案:

答案 0 :(得分:0)

  • 如果要运行,只需阅读Gmail收件箱中的标签,只需按照quickstart
  • 如果您想通过服务帐户执行相同的操作,则凭据的获得程度会有所不同,您需要
    • .p12文件(服务帐户凭据)粘贴到工作路径中,
    • 您需要在.setServiceAccountId中指定服务帐户的电子邮件,并且需要在.setServiceAccountUser中指定希望服务帐户代表的用户的电子邮件。
    • 如果您指定电子邮件-代码将列出您的标签;如果您让服务帐户冒充其他Google域用户,则该服务帐户可以访问他们的标签和电子邮件(前提是您启用了域范围内的委派)。

有效的示例代码:


    public static void main(String... args)   throws IOException, GeneralSecurityException
{
    final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();                           
     File pk12=new File("NAMEOFTHEFILE.p12");       
     String serviceAccount="EMAILOFTHESERVICEACCOUNT"; 
     GoogleCredential getCredentials = new GoogleCredential.Builder()
      .setTransport(HTTP_TRANSPORT)
      .setJsonFactory(JSON_FACTORY)
      .setServiceAccountId(serviceAccount)
      .setServiceAccountPrivateKeyFromP12File(pk12) 
      .setServiceAccountScopes(SCOPES)
      .setServiceAccountUser("MAILOFTHEUSERTOIMPERSONATE")  
      .build();

        // Build a new authorized API client service.
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials)
                .setApplicationName(APPLICATION_NAME)
                .build();
...

重要提示:您需要在管理控制台中为服务帐户提供其所需的所有范围,如文档的Delegating domain-wide authority to the service account部分所述。

答案 1 :(得分:0)

感谢ziganotschka,为您提供建议和帮助。我想使用服务帐户,因为我想将代码集成到现有的IMAP服务中。我已经启用了域范围的服务帐户委派。我尝试了您的建议,将setServiceAccountUser(EMAIL_USER)添加到GoogleCredential.Builder呼叫中。 偶然地,如果遵循GoogleCredential [https://github.com/googleapis/google-api-java-client/blob/master/google-api-client/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.java]中的代码, 可以使用JSON文件而不是p12文件(如您的示例)。

您的帮助使我能够进一步分析我的问题。如果我使用原始的Gmail电子邮件地址作为EMAIL_USER,那么我得到了TokenResponseException: 401 Unauthorized response。这个原始的Gmail地址 在我创建服务帐户所需创建的域中自然没有注册,并且似乎没有一种简单的方法(经过大量搜索后可以找到)在我的域中进行注册(将其称为 ds.de )。 如果我在网域中使用已注册的电子邮件地址(假设为 ds@ds.de )并尝试发送电子邮件,则会收到错误GoogleJsonResponseException Mail service not enabled,这也许并不奇怪,因为我的域名是 在Google世界之外。 然后,我想到了出于测试目的在Google世界中创建一个新域的方法-但Google不允许这样做,因为我住在德国,并且只有在一个人居住在特定国家/地区的情况下,才能通过Google创建一个域(https://support.google.com/domains/answer/4639612?hl=en) -德国不是其中之一!

我得出的结论是,因为我住在德国,所以无法使用Gmail API。因此,我将坚持在服务应用程序(已经成功运行了几年)中使用IMAP。我只是想升级到最新标准!

如果您还有其他建议,我很乐意尝试。