microsoftgraph / microsoftgraph/msgraph-sdk-dotnet

Sites.PostAsync returns null object

Open
#3,030 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Bug: metadata type:feature
Dominant language
C#
Stars
789
Forks
264
Avg merge
15h 17m
Merged PRs (30d)
3

Description

Describe the bug

Wanting to create a SharePoint site using Sites.PostAsync(), and waiting for the site to provision.

According to the Beta docs, this should return a site object, but it's null which makes sense as the site is created asynchronously. But the response contains a Location header which we can use to get the status of the site creation.

Unfortunately, the current iteration of the SDK (5.125.0-preview) doesn't allow access to this Location header.

Is there some funky thing we can do to the GraphServiceClient instance to get the Location header?

Expected behavior

The SDK to either:

  • Return a Tuple of the Site? and the Uri of the Location header, so that we can proceed to repeatedly ping this every 5 seconds or so until the site is created or otherwise errors.
  • Add property to the returned Site? instance, perhaps extended with a property that has the Uri of the Location header.
How to reproduce
                var site = await _delegatedGraphService.Sites.PostAsync(new GraphBeta.Models.Site
                {
                    Name = "My Newly Created Site",
                    DisplayName = "My Newly Created Site",
                    Description = "My Newly Created Site",
                    WebUrl = "https://tenant.sharepoint.com/sites/MyNewSite",
                    Locale = "en-US",
                    Template = SiteTemplateType.Sitepagepublishing
                });

                // This object instance is always null, but the site will be available after a few seconds
                var siteId = site.Id;
SDK Version

5.125.0-preview

Latest version known to work for scenario above?

No response

Known Workarounds

I managed to get a very clunky workaround in place.

  • Create a custom DelegatingHandler, this will test the response for a Location header and put its value into a public string property.
  • Instantiate an instance of this custom handler, and have it's instance publicly available in the class that is using GraphServiceClient
  • Create an instance of GraphServiceClient in a way that allows the use of the custom Delegating handler

When I make a request to Sites.PostAsync(), I then inspect the public string property of the custom DelegatingHandler to see if it's non-null - and consume that. I then use regex to extract just the operationId value.

    public class CustomGraphDelegatingHandler : DelegatingHandler
    {
        /// <summary>
        /// The value of the "Location" header from this request
        /// </summary>
        public string LocationHeader { get; set; } = string.Empty;

        /// <inheritdoc/>
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            LocationHeader = string.Empty;

            var response = await base.SendAsync(request, cancellationToken);
            if (response.Headers.TryGetValues("Location", out var values))
            {
                LocationHeader = values.First();
            }

            return response;
        }
    }

And then in the class that is setting up GraphServiceClient

   this.customHandler = new CustomGraphDelegatingHandler ();

   var handlers = GraphClientFactory.CreateDefaultHandlers();
   handlers.Add(this.customHandler);
   var http = GraphClientFactory.Create(handlers);
   graphServiceClient = new GraphBeta.GraphServiceClient(http, authProvider, graphEndpoint);

Thus, after making the call to Sites.PostAsync, I get the "Location" property out of this.customHandler, if it's there, use regex to pull out operationId, then issue a call to GetOperationStatusWithOperationId to check the status for "succeeded". If it's "Failed" I throw an exception, if it's anything else I sleep with Task.Delay(5000); then try again.

Pretty messy. Would be nice if there was a quicker way to get to the response object from the call, though.

Debug output

No response

Configuration

No response

Other information

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing Sites.PostAsync through the SDK response handling and compare it with the custom CustomGraphDelegatingHandler and GraphClientFactory setup described here. The change is done when callers can access the Location header from site creation and use it with GetOperationStatusWithOperationId to observe completion or failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.