DEV Community

Kenichiro Nakamura
Kenichiro Nakamura

Posted on

Identify which request failed in Microsoft Graph Batch request

Microsoft Graph provides Batch request feature which saves network round trips to improve performance.

We can include different types of requests, such as GET/POST/DELETE or resources such as Users/Groups/Events, etc.

Even though this is useful, it could be difficult to identify the reason why some of the request has been failed depending on the request type.

Example Scenario

If we request batch to remove users from a group, for example, it will fail if the user is not part of the group. However, the error message does not give us information about which user id was failed to remove.

This is the error when the request fails.

"error": {
    "code": "Request_ResourceNotFound",
    "message": "Resource 'be24f653-c890-454c-a465-0297c3de2fc2' does not exist or one of its queried reference-property objects are not present.",
    "innerError": {
        "date": "2022-10-03T04:06:29",
        "request-id": "af2627a2-428d-4061-bb3a-00247e8589cc",
        "client-request-id": "e03aab35-0657-c9e2-6703-c38debae955c"
    }
}
Enter fullscreen mode Exit fullscreen mode

In such case, we need to know actual request itself to know user id.

Unique Request Id in batch

We need to add BatchRequestStep list to batch request, and each BatchRequestStep has RequestId property. This id will be part of response.

Sample code

Following sample explain how to check response as well as request. I only added two requests for simplify the code, but we can add more. We can set any unique request id, so I used user id here, which I use to show in console as user id.

The Key of batch response is corresponding to request id.

using Microsoft.Graph;
using Microsoft.Identity.Client;
using System.Net;
using System.Net.Http.Headers;

string tenantId = "tenantId";
string clientId = "clientId";
string clientSecret = "clientSecret";
string baseUrl = "https://graph.microsoft.com/v1.0";
IConfidentialClientApplication app = 
    ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientSecret(clientSecret)
    .WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
    .Build();

string[] scopes = new[] { "https://graph.microsoft.com/.default" };

AuthenticationResult authenticationResult = 
    await app.AcquireTokenForClient(scopes).ExecuteAsync();

GraphServiceClient graphClient = new (
    new DelegateAuthenticationProvider(r => {
        r.Headers.Authorization =
            new AuthenticationHeaderValue(
                "bearer", authenticationResult.AccessToken);
        return Task.FromResult(0);
    }));

List<User> users = new();
BatchRequestContent batchRequestContent = new();
string groupId = "the groupId";
string memberId1 = "the memberId1";
batchRequestContent.AddBatchRequestStep(
    new BatchRequestStep(
        memberId1,
        new HttpRequestMessage(
            HttpMethod.Delete,
            $"{baseUrl}/groups/{groupId}/members/{memberId1}/$ref")
        )
    );
string memberId2 = "the memberId2";
batchRequestContent.AddBatchRequestStep(
    new BatchRequestStep(
        memberId2,
        new HttpRequestMessage(
            HttpMethod.Delete,
            $"{baseUrl}/groups/{groupId}/members/{memberId2}/$ref")
        )
    );

BatchResponseContent batchResponseContent = 
    await graphClient.Batch.Request().PostAsync(batchRequestContent);

foreach(KeyValuePair<string, HttpResponseMessage> response 
    in await batchResponseContent.GetResponsesAsync())
{
    if(response.Value.StatusCode != HttpStatusCode.OK)
    {
        BatchRequestStep requestStep = batchRequestContent.BatchRequestSteps[response.Key];
        Console.WriteLine($"StatusCode: {response.Value.StatusCode}");
        Console.WriteLine($"Request URI: {requestStep.Request.RequestUri}");
        Console.WriteLine($"Failed user: {requestStep.RequestId}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

Batch is really useful feature for Microsoft Graph, so please try to use it as much as possible. For unit testing the batch request, I have another post here

Top comments (0)