Easily debug Microsoft Graph .NET SDK requests


Using the Microsoft Graph .NET SDK is a convenient way to connect your app to Microsoft 365. Here’s how you can easily debug API requests issued from the SDK.

Build apps that work with Microsoft 365

Microsoft 365 is a platform where millions of users work together every day. You can create apps that use the data and insights from Microsoft 365 to help them do their work more efficiently and effectively.

Microsoft Graph is the API that connects you to your organization’s data in Microsoft 365. You can access it using its REST endpoints. If you build an app connected to Microsoft 365 using .NET, you should consider using the Microsoft Graph .NET SDK. The SDK makes authentication easier and handles complex scenarios like backing off when throttled, following redirects and uploading large files, which means that you can focus on building your app instead of its plumbing!

Use the Microsoft Graph .NET SDK to connect your .NET app to Microsoft 365

To begin using the Microsoft Graph .NET SDK, create a Graph client with an authentication provider. Then, you can start calling Microsoft Graph to access and manipulate data in Microsoft 365.

For building a deamon app, you’d use code similar to following:

using Azure.Identity;
using Microsoft.Graph;

var credential = new ClientSecretCredential(
  tenantId,
  clientId,
  clientSecret
);
var scopes = new[] { "https://graph.microsoft.com/.default" };
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);

Debug Microsoft Graph .NET SDK API requests

When you start building your app, it can happen that you’ll get an error calling Microsoft Graph. Looking at the returned error information should give you a clearer idea of what’s wrong and how to fix it.

If that’s not the case though, and you need to see the request and response that goes over the wire, here’s a trick that you can use.

Microsoft Graph .NET SDK middleware

The Microsoft Graph .NET SDK supports the concept of middleware: functions that run as part of its request- and response pipeline and have access to the information about the outgoing request and received response. If you want to inspect API requests and responses issued by the Microsoft Graph .NET SDK, building a custom middleware is the easiest way about it. Middleware has access to the full request context information which includes information about the outgoing request and received response.

Debug middleware for Microsoft Graph .NET SDK

To create debug middleware for the Microsoft Graph .NET SDK, you have to split it into request- and response middleware. This has to do with how the middleware in the .NET SDK is ordered and called. Handlers are invoked in a top-down fashion. The first entry is invoked first for an outbound request message but last for an inbound response message.

To get full information about the outgoing request, you want the debug middleware to be the last middleware in the chain. But since decompression is done by the first middleware in the chain, to get access to human-readable response body, you need to add the debug middleware before the decompression middleware, which means adding it at the beginning of the collection.

Log the request information

Start, with creating a new file named DebugRequestHandler.cs. Add the following code:

class DebugRequestHandler : DelegatingHandler
{
  protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    Console.WriteLine("");
    Console.WriteLine(string.Format("Request: {0} {1}", request.Method, request.RequestUri));
    Console.WriteLine("Request headers:");
    foreach (var header in request.Headers)
    {
      Console.WriteLine(string.Format("{0}: {1}", header.Key, string.Join(',', header.Value)));
    }
    if (request.Content is not null)
    {
      Console.WriteLine("");
      Console.WriteLine("Request body:");
      var body = await request.Content.ReadAsStringAsync();
      Console.WriteLine(body);
    }

    return await base.SendAsync(request, cancellationToken);
  }
}

The function follows the Microsoft Graph .NET SDK middleware pattern. It inherits from the DelegatingHandler base class and overrides the SendAsync method.

When the SendAsync method is invoked, it will log the request method and URI. Next, it will list all request headers. Finally, if the request contains a body, it will print it too. In the end, it will pass the request to the middleware chain by calling base.SendAsync().

Log the response information

Next, create the DebugResponseHandler.cs file where you’ll implement the middleware to log information about the received response. Add the following code:

class DebugResponseHandler : DelegatingHandler
{
  protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    var response = await base.SendAsync(request, cancellationToken);

    Console.WriteLine("");
    Console.WriteLine("Response headers:");
    foreach (var header in response.Headers)
    {
      Console.WriteLine(string.Format("{0}: {1}", header.Key, string.Join(',', header.Value)));
    }
    if (response.Content is not null)
    {
      Console.WriteLine("");
      Console.WriteLine("Response body:");
      var body = await response.Content.ReadAsStringAsync();
      Console.WriteLine(body);
    }

    return response;
  }
}

The execution starts with getting a response from the middleware chain by calling base.SendAsync().

Then, the middleware iterates over all response headers and prints them to console. If the response contains a body, it will print it out too.

Finally, it returns the response to the middleware chain.

Use the debug middleware

The last step is to add the debug middleware to the Graph client. To do that, update the client’s initiation code as follows:

using Azure.Identity;
using Microsoft.Graph;

var credential = new ClientSecretCredential(
  tenantId,
  clientId,
  clientSecret
);
var handlers = GraphClientFactory.CreateDefaultHandlers();
// add at the end to get all information about the request
handlers.Add(new DebugRequestHandler());
// add at the beginning to get all information about the response
// and also have the response body decompressed
handlers.Insert(0, new DebugResponseHandler());
var httpClient = GraphClientFactory.Create(handlers);
var graphClient = new GraphServiceClient(httpClient, credential);

We changed the Graph client instantiation code, to get the default middleware chain. Like I mentioned before, to get access to the full request information, the DebugRequestHandler must be the last handler in the collection. To get access to the decompressed response body, the DebugResponseHandler must be the first in the collection, right before the middleware responsible for decompression.

With this setup in place, when you call Microsoft Graph using the client, you’ll see the following output in the console:

TIP: To see this middleware in action, check out the sample Microsoft Graph connector.

dotnet run

Request: GET https://graph.microsoft.com/v1.0/users?$top=1
Request headers:
Accept: application/json
Authorization: Bearer eyJ0eXAiOiJKV1Qi...
FeatureFlag: 00000043
Cache-Control: no-store, no-cache
Accept-Encoding: gzip
User-Agent: kiota-dotnet/1.1.1
SdkVersion:  graph-dotnet-core/3.0.11 (featureUsage=0000002B; hostOS=Unix 14.0.0; hostArch=X64; runtimeEnvironment=.NET 7.0.3;)
client-request-id: bfbfd8a5-ce70-4950-83cf-851fb9a91c0a

Response headers:
Cache-Control: no-cache
Transfer-Encoding: chunked
Vary: Accept-Encoding
Strict-Transport-Security: max-age=31536000
request-id: efb7b970-283f-4afc-9ce4-f28b0755940b
client-request-id: bfbfd8a5-ce70-4950-83cf-851fb9a91c0a
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"West Europe","Slice":"E","Ring":"5","ScaleUnit":"004","RoleInstance":"AM2PEPF0001D784"}}
x-ms-resource-unit: 1
OData-Version: 4.0
pubDate: Fri, 13 Oct 2023 13:14:25 GMT

Response body:
{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#users","@odata.nextLink":"https://graph.microsoft.com/v1.0/users?$top=1&$skiptoken=RFNwdAIAAQAAAB46QWRlbGVWQDJjemszZy5vbm1pY3Jvc29mdC5jb20pVXNlcl82ZGU4ZWMwNC02Mzc2LTQ5MzktYWI0Ny04M2EyYzg1YWI1ZjW5AAAAAAAAAAAAAA","value":[{"businessPhones":["+1 425 555 0109"],"displayName":"Adele Vance","givenName":"Adele","jobTitle":"Retail Manager","mail":"AdeleV@2czk3g.onmicrosoft.com","mobilePhone":null,"officeLocation":"18/2111","preferredLanguage":"en-US","surname":"Vance","userPrincipalName":"AdeleV@2czk3g.onmicrosoft.com","id":"6de8ec04-6376-4939-ab47-83a2c85ab5f5"}]}

Summary

Using the Microsoft Graph .NET SDK is an easy way for you to get the data and insights from Microsoft 365 in your app. The SDK simplifies authentication and handles complex API scenarios for you. If you ever need to debug the requests and responses handled by the SDK, you can build a simple debug middleware to log the raw requests and responses for you.

Others found also helpful: