Search code examples
c#microsoft-graph-apimicrosoft-graph-sdks

How to access response header in Graph v5.0 SDK?


While I'm trying to upgrade to the Graph SDK to v5.0 I'm running into an issue when upgrading the password reset of a user. According to the documenation you can send a user defined password like this:

POST https://graph.microsoft.com/v1.0/users/6ea91a8d-e32e-41a1-b7bd-d2d185eed0e0/authentication/passwordMethods/28c10230-6103-485e-b985-444c60001490/resetPassword
Content-type: application/json

{
    "newPassword": "Cuyo5459"
}

Unfortunately this call is currently not supported within the SDK and we have to manually build the request and send it:

var requestInfo = serviceClient.Users[userId].Authentication.PasswordMethods[passwordMethod.Id].ToGetRequestInformation();
requestInfo.HttpMethod = Microsoft.Kiota.Abstractions.Method.POST;
requestInfo.URI = new Uri(requestInfo.URI, "/resetPassword");
requestInfo.Content = new MemoryStream(Encoding.UTF8.GetBytes($"{{\"newPassword\":\"{password}\"}}"));

The problem arises when it comes to read the response. According to the documenation you'll get an empty body and the header contains a location URL:

HTTP/1.1 202 Accepted
Content-type: application/json
Location: https://graph.microsoft.com/v1.0/users/6ea91a8d-e32e-41a1-b7bd-d2d185eed0e0/authentication/operations/88e7560c-9ebf-435c-8089-c3998ac1ec51?aadgdc=DUB02P&aadgsu=ssprprod-a

{}

But if we send the request by ourself through the request adapter of the graph service client, we can't access the response header:

var passwordResult = await serviceClient.RequestAdapter.SendAsync(requestInfo, PasswordResetResponse.CreateFromDiscriminatorValue);

According to the Kiota documenation it should be possible to register an IResponseHandler and that could probably access this information, but I can't find anything about how to register such an handler for a single specific request in the graph service client. Also in the documentation you can provide through the errorMapping parameter special serializers depending on the response status code, but even this would be just a ParsableFactory<> that can only access the body and not the header of the response (if I understood it correctly).

So what approach can be taken by using the v5 Graph SDK to access the response header?


Solution

  • By looking into the AsyncMonitor<T> code we can see, that a NativeResponseHandler can be used when performing raw request information:

    var requestInformation = new RequestInformation() { HttpMethod = Method.GET , UrlTemplate = this.monitorUrl};
    var nativeResponseHandler = new NativeResponseHandler();
    requestInformation.SetResponseHandler(nativeResponseHandler);
    await this.client.RequestAdapter.SendNoContentAsync(requestInformation, cancellationToken:cancellationToken).ConfigureAwait(false);
    using var responseMessage = nativeResponseHandler.Value as HttpResponseMessage;
    

    In my given code example it would look like this:

    var nativeResponseHandler = new NativeResponseHandler();
    var requestInfo = serviceClient.Users[userInformations.UserPrincipalName].Authentication.PasswordMethods[passwordMethod.Id].ToGetRequestInformation();
    requestInfo.HttpMethod = Method.POST;
    requestInfo.URI = new Uri(requestInfo.URI, "/resetPassword");
    requestInfo.Content = new MemoryStream(Encoding.UTF8.GetBytes($"{{\"newPassword\":\"{password}\"}}"));
    requestInfo.SetResponseHandler(nativeResponseHandler);
    
    var passwordResult = await serviceClient.RequestAdapter.SendAsync(requestInfo, PasswordResetResponse.CreateFromDiscriminatorValue);
    using var responseMessage = nativeResponseHandler.Value as HttpResponseMessage;
    var location = responseMessage.Headers.Location;
    

    To use this stuff within the graph client, when the desired method is available but you need to additionally access the response header, you could do this with code similar to:

    var nativeResponseHandler = new NativeResponseHandler();
    await service.Drives[drive.Id].Items[sourceItem.Id].Copy.PostAsync(new CopyPostRequestBody
    {
        Name = sourceItem.Name,
        ParentReference = reference,
    },
    config => config.Options.Add(new ResponseHandlerOption { ResponseHandler = nativeResponseHandler }));
    
    using var responseMessage = nativeResponseHandler.Value as HttpResponseMessage;
    var location = responseMessage.Headers.Location;
    

    The information about how to add the ResponseHandlerOption has been added to the v5 upgrade guide.