1
votes

I am attempting to write some code that fetches emails from Gmail using the Gmail API documented here. I have successfully managed to retrieve a list of emails, and to get details of individual emails.

What I would like to do now is to use a BatchRequest to get details of multiple emails in a single call, but when I try this I get a 401 error with the message:

Google.Apis.Requests.RequestError Login Required [401] Errors [ Message[Login Required] Location[Authorization - header] Reason[required] Domain[global] ]

In the GetMessageInfo method in the class below, there are three calls to the API:

  1. Messages.List This successfully returns a list of messages
  2. Messages.Get This successfully returns details of a single message
  3. Finally, I attempt the same Messages.Get as in Step 2, but this time using a BatchRequest, and this fails with the above error.

I am using the same service object each time, and in the case of Steps 2 & 3, I am using the same request.

QUESTION: Why can I get message details with a single request but not as part of a batch request?

public class ProofOfConcept
{
  public void GetMessageInfo()
  {
    GmailService service = GetService();

    UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List("me");
    request.MaxResults = 1;

    ListMessagesResponse response = request.Execute();
    Message message = response.Messages.First();

    Message fullMessageBySingleRequest = PerformSingleRequest(service, message.Id);
    Message fullMessageByBatchRequest = PerformBatchRequest(service, message.Id, out RequestError error);
  }

  private Message PerformSingleRequest(GmailService service, string messageId)
  {
    UsersResource.MessagesResource.GetRequest request = service.Users.Messages.Get("me", messageId);
    Message message = request.Execute();

    return message;
  }

  private Message PerformBatchRequest(GmailService service, string messageId, out RequestError err)
  {
    UsersResource.MessagesResource.GetRequest messageRequest = service.Users.Messages.Get("me", messageId);

    var batchRequest = new BatchRequest(service);

    Message message = null;
    RequestError requestError = null;

    batchRequest.Queue<Message>(
      messageRequest,
      (content, error, i, msg) =>
      {
        message = content;
        requestError = error;
      });

    batchRequest.ExecuteAsync().Wait();

    err = requestError;
    return message;
  }

  private GmailService GetService()
  {
    UserCredential credential;

    using (var stream = new FileStream(
      @".\ClientSecret\client_secret.json", 
      FileMode.Open, 
      FileAccess.Read))
    {
      string credPath = Environment.GetFolderPath(
        Environment.SpecialFolder.Personal);

      credPath = Path.Combine(
        credPath, 
        ".credentials/gmail-dotnet-quickstart.json");

      credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
          GoogleClientSecrets.Load(stream).Secrets,
          new[] {GmailService.Scope.GmailReadonly},
          "user",
          CancellationToken.None,
          new FileDataStore(credPath, true))
        .Result;
    }

    // Create Gmail API service.
    var service = new GmailService(new BaseClientService.Initializer
    {
      HttpClientInitializer = credential,
      ApplicationName = "Foo"
    });

    return service;
  }
}
1
Why exactly do you want to do this with batching? Why not just send the requests one at a time? That being said this is the only documentation we have on batching with the client library developers.google.com/api-client-library/dotnet/guide/batch it may helpDaImTo
That's a good question. I'd be interested to know what common practice is with the gmail api. In answer, I will be wanting to send hundreds of requests, and while I could do this with individual requests, batching seems to be the recommended way of doing it. There are also some arguments for why you would do it on the page you link to (which, incidentally, is the same page I linked to, and what I based my own code on).bornfromanegg
This code should be working, at least nowadays. My code is pretty much like yours, and it works. The only different is that I use a service account when providing the credentials, however, since your credentials worked for the other requests, it should be working for the batch request as well.Alisson

1 Answers

-1
votes

OK this got to big for a comment.

For starters I have never bothered with batching on the Google apis. The code linked I have never gotten to work properly (Imo no fault of the code), I have tried it with several Google APIs(analytics and drive). I have never found the Google batching end point to be very stable. If one request in the batch request fails or is to slow then they all fail you will get a 500 error back.

Second you are not really going to win anything using batching IMO. The quota hits will be the same. Sending five requests one at a time and sending one batch with the same five requests is still going to be five against your quota. Also there is a limit to how many requests you can put in a batch request it depends on the API. You cant put 100 requests in a batch request i think the limit is more like 10.

The only thing you may win is the back and forth on the server which if you have a faster internet connection shouldn't really matter.

All that being said if you really want to spend the time getting batching working let me know i would be happy to give it a try again. I just think you should consider wither its worth your time or not.