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"
}
}
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}");
}
}
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)