{"id":2058,"date":"2018-11-15T11:00:07","date_gmt":"2018-11-15T11:00:07","guid":{"rendered":"https:\/\/developer.microsoft.com\/en-us\/office\/blogs\/?p=2058"},"modified":"2018-11-15T11:00:07","modified_gmt":"2018-11-15T11:00:07","slug":"30daysmsgraph-day-15-microsoft-graph-in-dotnet-core-application","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/30daysmsgraph-day-15-microsoft-graph-in-dotnet-core-application\/","title":{"rendered":"30DaysMSGraph \u2013 Day 15 \u2013 Microsoft Graph in .NET Core application"},"content":{"rendered":"<p><a href=\"https:\/\/aka.ms\/30DaysMSGraph\">List of all posts in the #30DaysMSGraph series<\/a><\/p>\n<p>In <a href=\"https:\/\/developer.microsoft.com\/en-us\/graph\/blogs\/30daysmsgraph-day-14-batch-processing\">Day 14<\/a> we discussed batch processing requests to Microsoft Graph requests.\u00a0 Today we&#8217;ll take a big jump and build our first project in .NET Core that can make calls against Microsoft Graph.\u00a0 We recommend you bookmark this day as we&#8217;ll be building upon this process for many of the remaining days while we demo various endpoints within the Microsoft.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-2118\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2018\/11\/30DaysMSGraph_Day15_Source-1024x683.jpg\" alt=\"\" width=\"799\" height=\"533\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2018\/11\/30DaysMSGraph_Day15_Source-1024x683.jpg 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2018\/11\/30DaysMSGraph_Day15_Source-300x200.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2018\/11\/30DaysMSGraph_Day15_Source-768x512.jpg 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2018\/11\/30DaysMSGraph_Day15_Source-1536x1024.jpg 1536w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2018\/11\/30DaysMSGraph_Day15_Source-2048x1366.jpg 2048w\" sizes=\"(max-width: 799px) 100vw, 799px\" \/><\/p>\n<p>For those of you who are not familiar with Visual Studio or Visual Studio Code, don&#8217;t worry.\u00a0 The goal of this sample project for today is to build a base .NET Core console application utilizing a minimum number of steps to authenticate and call Microsoft Graph.\u00a0 We are adapting the instructions from the <a href=\"https:\/\/github.com\/microsoftgraph\/dotnetcore-console-sample\">dotnetcore-console-sample<\/a> repo for the instructions below as that repo may change over time.<\/p>\n<p><em><strong>Note:<\/strong>\u00a0The repo README file will be a cleaner version of the instructions due to formatting limitations with this blog post.\u00a0 Also, today&#8217;s post will likely take longer than the usual 5-15 mins of the other daily posts.\u00a0 If you wish to &#8220;skip ahead&#8221; you may clone the sample repo and implement the necessary steps (create Azure AD app, assign and grant permissions to Azure AD app, configure appsettings.json file, etc.) to run the project.<\/em><\/p>\n<h2>Prerequisites<\/h2>\n<p>To complete this sample, you need the following:<\/p>\n<ul>\n<li><a href=\"https:\/\/code.visualstudio.com\/Download\">Visual Studio Code<\/a> installed on your development machine. If you do not have Visual Studio Code, visit the previous link for download options. (<strong>Note<\/strong>: This tutorial was written with Visual Studio Code version 1.28.2. The steps in this guide may work with other versions, but that has not been tested.)<\/li>\n<li><a href=\"https:\/\/www.microsoft.com\/net\/download\/dotnet-core\/2.1#sdk-2.1.403\">.NET Core SDK<\/a>. (<strong>Note<\/strong>: This tutorial was written with .NET Core SDK 2.1.403. The steps in this guide may work with other versions, but that has not been tested.)<\/li>\n<li><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-vscode.csharp\">C# extension for Visual Studio Code<\/a><\/li>\n<li>A Microsoft work or school account.<\/li>\n<\/ul>\n<p>If you don&#8217;t have a Microsoft work or school account there is an option to get a free account:<\/p>\n<ul>\n<li>You can <a href=\"https:\/\/developer.microsoft.com\/office\/dev-program\">sign up for the Office 365 Developer Program<\/a> to get a free Office 365 subscription.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h3>Step 1: Create a .NET Core Console Application<\/h3>\n<ol>\n<li>Create a folder called\u00a0<strong>ConsoleGraphTest<\/strong>\u00a0for the console application.<\/li>\n<\/ol>\n<p><strong>Note:<\/strong>\u00a0For the purposes of this sample the project folder was named\u00a0<strong>ConsoleGraphTest<\/strong>. If you choose a different folder name, ensure that the namespace for files matches.<\/p>\n<p>Open the command line and navigate to this folder. Run the following command:<\/p>\n<pre style=\"padding-left: 30px\">dotnet new console<\/pre>\n<p>Before moving on, install the following NuGet packages that you will use later.<\/p>\n<ul>\n<li>Identity.Client<\/li>\n<li>Graph<\/li>\n<li>Extensions.Configuration<\/li>\n<li>Extensions.Configuration.FileExtensions<\/li>\n<li>Extensions.Configuration.Json<\/li>\n<\/ul>\n<p>Run the following commands to install these NuGet packages:<\/p>\n<pre style=\"padding-left: 30px\">dotnet add package Microsoft.Identity.Client --version 2.3.1-preview\ndotnet add package Microsoft.Graph\ndotnet add package Microsoft.Extensions.Configuration\ndotnet add package Microsoft.Extensions.Configuration.FileExtensions\ndotnet add package Microsoft.Extensions.Configuration.Json<\/pre>\n<h3>Step 2: Register a web application with the new Azure AD Portal App Registration<\/h3>\n<p>Either re-use the Azure AD app registration from <a href=\"https:\/\/developer.microsoft.com\/en-us\/graph\/blogs\/30daysmsgraph-day-9-azure-ad-applications-on-v2-endpoint\">Day 9<\/a> or follow those steps again to register a new application.\u00a0 The following settings will need to be configured:<\/p>\n<ul>\n<li>Name = .NET Core Console App Test<\/li>\n<li>Redirect URI = <a href=\"https:\/\/localhost:8080\/\">https:\/\/localhost:8080<\/a><\/li>\n<li>Permissions assigned and admin consented:\n<ul>\n<li>Microsoft Graph &#8211; User.Read.All<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>Step 3: Extend the app for Azure AD Authentication<\/h3>\n<p><strong>Create configuration file<\/strong><\/p>\n<p>In this step you will extend the application from the previous step to support authentication with Azure AD. This is required to obtain the necessary OAuth access token to call the Microsoft Graph. In this step you will integrate the\u00a0<a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Identity.Client\/\">Microsoft Authentication Library<\/a>\u00a0library into the application.<\/p>\n<p>On the command line from Step 1, run the following command inside the project folder to open Visual Studio Code with the project folder opened:<\/p>\n<pre style=\"padding-left: 30px\">code .<\/pre>\n<p>Add a file to the folder named\u00a0<strong>appsettings.json<\/strong>\u00a0with the following content:<\/p>\n<pre style=\"padding-left: 30px\">{\n\"applicationId\": \"YOUR_APP_ID_HERE\",\n\"applicationSecret\": \"YOUR_APP_SECRET_HERE\",\n\"tenantId\": \"YOUR_TENANT_ID_HERE\",\n\"redirectUri\": \"YOUR_REDIRECT_URI_HERE\"\n}<\/pre>\n<p>Edit\u00a0<strong>appsettings.json<\/strong>\u00a0and fill in the values obtained in previous step on the Azure AD Portal app registration UI:<\/p>\n<ul>\n<li>Replace\u00a0YOUR_APP_ID_HERE\u00a0with your application ID.<\/li>\n<li>Replace\u00a0YOUR_APP_SECRET_HERE\u00a0with your client secret (VALUE from Secret1 in previous steps).<\/li>\n<li>Replace\u00a0YOUR_TENANT_ID_HERE\u00a0with your tenant (domain) ID.<\/li>\n<li>Replace\u00a0YOUR_REDIRECT_URI_HERE\u00a0with your application redirect URI.<\/li>\n<\/ul>\n<p><strong>Important:<\/strong>\u00a0If you&#8217;re using source control such as git, now would be a good time to exclude the\u00a0<strong>appsettings.json<\/strong>\u00a0file from source control to avoid inadvertently leaking your app ID and secret.<\/p>\n<p><strong>Create helper classes<\/strong><\/p>\n<p>Create a new folder called\u00a0<strong>Helpers<\/strong>.<\/p>\n<p>Create a new file in the\u00a0<strong>Helpers<\/strong>\u00a0folder called\u00a0<strong>AuthHandler.cs<\/strong>.<\/p>\n<p>Replace the contents of\u00a0<strong>AuthHandler.cs<\/strong>\u00a0with the following code:<\/p>\n<pre style=\"padding-left: 30px\">using System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Threading.Tasks;\nusing Microsoft.Identity.Client;\nusing Microsoft.Graph;\nusing Microsoft.Extensions.Configuration;\nusing System.Linq;\nusing System.Threading;\n\nnamespace ConsoleGraphTest\n{\n\/\/ This class allows an implementation of IAuthenticationProvider to be inserted into the DelegatingHandler\n\/\/ pipeline of an HttpClient instance.\u00a0 In future versions of GraphSDK, many cross-cutting concernts will\n\/\/ be implemented as DelegatingHandlers.\u00a0 This AuthHandler will come in the box.\npublic class AuthHandler : DelegatingHandler {\nprivate IAuthenticationProvider _authenticationProvider;\n\npublic AuthHandler(IAuthenticationProvider authenticationProvider, HttpMessageHandler innerHandler)\n{\nInnerHandler = innerHandler;\n_authenticationProvider = authenticationProvider;\n}\n\nprotected override async Task&lt;HttpResponseMessage&gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n{\nawait _authenticationProvider.AuthenticateRequestAsync(request);\nreturn await base.SendAsync(request,cancellationToken);\n}\n}\n}<\/pre>\n<p>Create a new file in the\u00a0<strong>Helpers<\/strong>\u00a0folder called\u00a0<strong>MsalAuthenticationProvider.cs<\/strong><\/p>\n<p>Replace the contents of\u00a0<strong>MsalAuthenticationProvider.cs\u00a0<\/strong>with the following code:<\/p>\n<pre style=\"padding-left: 30px\">using System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Threading.Tasks;\nusing Microsoft.Identity.Client;\nusing Microsoft.Graph;\nusing Microsoft.Extensions.Configuration;\nusing System.Linq;\n\nnamespace ConsoleGraphTest\n{\n\/\/ This class encapsulates the details of getting a token from MSAL and exposes it via the\n\/\/ IAuthenticationProvider interface so that GraphServiceClient or AuthHandler can use it.\n\/\/ A significantly enhanced version of this class will in the future be available from\n\/\/ the GraphSDK team.\u00a0 It will supports all the types of Client Application as defined by MSAL.\npublic class MsalAuthenticationProvider : IAuthenticationProvider\n{\nprivate ConfidentialClientApplication _clientApplication;\nprivate string[] _scopes;\n\npublic MsalAuthenticationProvider(ConfidentialClientApplication clientApplication, string[] scopes) {\n_clientApplication = clientApplication;\n_scopes = scopes;\n}\n\n\/\/\/ &lt;summary&gt;\n\/\/\/ Update HttpRequestMessage with credentials\n\/\/\/ &lt;\/summary&gt;\npublic async Task AuthenticateRequestAsync(HttpRequestMessage request)\n{\nvar token = await GetTokenAsync();\nrequest.Headers.Authorization = new AuthenticationHeaderValue(\"bearer\", token);\n}\n\n\/\/\/ &lt;summary&gt;\n\/\/\/ Acquire Token\n\/\/\/ &lt;\/summary&gt;\npublic async Task&lt;string&gt; GetTokenAsync()\n{\nAuthenticationResult authResult = null;\nauthResult = await _clientApplication.AcquireTokenForClientAsync(_scopes);\nreturn authResult.AccessToken;\n}\n}\n}<\/pre>\n<h3>Step 4: Extend the app for Microsoft Graph<\/h3>\n<p>In this step you will incorporate the Microsoft Graph into the application. For this application, you will use the\u00a0<a href=\"https:\/\/github.com\/microsoftgraph\/msgraph-sdk-dotnet\">Microsoft Graph Client Library for .NET<\/a>\u00a0to make calls to Microsoft Graph.<\/p>\n<p><strong>Get user information from tenant<\/strong><\/p>\n<p>Opening the\u00a0<strong>Program.cs<\/strong>\u00a0file. Add the following &#8220;using&#8221; statements to the top of the file.<\/p>\n<pre style=\"padding-left: 30px\">using System;\nusing System.Collections.Generic;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Threading.Tasks;\nusing Microsoft.Identity.Client;\nusing Microsoft.Graph;\nusing Microsoft.Extensions.Configuration;<\/pre>\n<p>Ensure that the namespace matches across the project. In this example we have used\u00a0<strong>ConsoleGraphTest<\/strong>. Update the namespace accordingly if needed.<\/p>\n<p>Inside the\u00a0<strong>Program<\/strong>\u00a0class add static references to\u00a0<strong>GraphServiceClient<\/strong>\u00a0and\u00a0<strong>HttpClient<\/strong>. These static variables can be used to instantiate the clients used to make calls against the Microsoft Graph.<\/p>\n<pre style=\"padding-left: 30px\">private static GraphServiceClient _graphServiceClient;\nprivate static HttpClient _httpClient;<\/pre>\n<p>Inside the\u00a0Program\u00a0class add a new method\u00a0LoadAppSettings\u00a0with the following definition. This method retrieves the configuration values from a separate file. This allows updating the configuration (client Id, client secret, etc.) independently of the code itself. This is a general best practice when possible to separate configuration from code.<\/p>\n<pre style=\"padding-left: 30px\">private static IConfigurationRoot LoadAppSettings()\n{\ntry\n{\nvar config = new ConfigurationBuilder()\n.SetBasePath(System.IO.Directory.GetCurrentDirectory())\n.AddJsonFile(\"appsettings.json\", false, true)\n.Build();\n\n\/\/ Validate required settings\nif (string.IsNullOrEmpty(config[\"applicationId\"]) ||\nstring.IsNullOrEmpty(config[\"applicationSecret\"]) ||\nstring.IsNullOrEmpty(config[\"redirectUri\"]) ||\nstring.IsNullOrEmpty(config[\"tenantId\"]))\n{\nreturn null;\n}\n\nreturn config;\n}\ncatch (System.IO.FileNotFoundException)\n{\nreturn null;\n}\n}<\/pre>\n<p>Inside the\u00a0Program\u00a0class add a new method\u00a0<strong>CreateAuthorizationProvider<\/strong>\u00a0that will be used in later methods to instantiate the clients used for making calls against the Microsoft Graph. This method uses the configuration data with a\u00a0<strong>ConfidentialClientApplication<\/strong>.<\/p>\n<pre style=\"padding-left: 30px\">private static IAuthenticationProvider CreateAuthorizationProvider(IConfigurationRoot config)\n{\nvar clientId = config[\"applicationId\"];\nvar clientSecret = config[\"applicationSecret\"];\nvar redirectUri = config[\"redirectUri\"];\nvar authority = $\"https:\/\/login.microsoftonline.com\/{config[\"tenantId\"]}\/v2.0\";\n\nList&lt;string&gt; scopes = new List&lt;string&gt;();\nscopes.Add(\"https:\/\/graph.microsoft.com\/.default\");\n\nvar cca = new ConfidentialClientApplication(clientId, authority, redirectUri, new ClientCredential(clientSecret), null, null);\nreturn new MsalAuthenticationProvider(cca, scopes.ToArray());\n}<\/pre>\n<p>Inside the\u00a0<strong>Program<\/strong>\u00a0class add a new method\u00a0<strong>GetAuthenticatedGraphClient<\/strong>\u00a0with the following definition. This method creates an instance of the\u00a0<strong>GraphServiceClient<\/strong>\u00a0from the static reference. The\u00a0<strong>GraphServiceClient<\/strong>\u00a0instance uses the configuration returned from previous method.<\/p>\n<pre style=\"padding-left: 30px\">private static GraphServiceClient GetAuthenticatedGraphClient(IConfigurationRoot config)\n{\nvar authenticationProvider = CreateAuthorizationProvider(config);\n_graphServiceClient = new GraphServiceClient(authenticationProvider);\nreturn _graphServiceClient;\n}<\/pre>\n<p>Inside the\u00a0Program\u00a0class add a new method\u00a0<strong>GetAuthenticatedHTTPClient<\/strong>\u00a0with the following definition. This method creates an instance of the\u00a0<strong>HTTPClient<\/strong>\u00a0from the static reference. The\u00a0<strong>HTTPClient<\/strong>\u00a0instance uses the configuration returned from previous method.<\/p>\n<pre style=\"padding-left: 30px\">private static HttpClient GetAuthenticatedHTTPClient(IConfigurationRoot config)\n{\nvar authenticationProvider = CreateAuthorizationProvider(config);\n_httpClient = new HttpClient(new AuthHandler(authenticationProvider, new HttpClientHandler()));\nreturn _httpClient;\n}<\/pre>\n<p>Inside the\u00a0<strong>Main<\/strong>\u00a0method add the following to load the configuration settings.<\/p>\n<pre style=\"padding-left: 30px\">var config = LoadAppSettings();\nif (null == config)\n{\nConsole.WriteLine(\"Missing or invalid appsettings.json file. Please see README.md for configuration instructions.\");\nreturn;\n}<\/pre>\n<p>Continuing in the\u00a0<strong>Main<\/strong>\u00a0method add the following to get an authenticated instance of the\u00a0<strong>GraphServiceClient<\/strong>\u00a0and send a request to retrieve the first user from &#8220;\/Users&#8221; endpoint on the Microsoft Graph.<\/p>\n<pre style=\"padding-left: 30px\">\/\/Query using Graph SDK (preferred when possible)\nGraphServiceClient graphClient = GetAuthenticatedGraphClient(config);\nList&lt;QueryOption&gt; options = new List&lt;QueryOption&gt;\n{\n\u00a0\u00a0\u00a0 new QueryOption(\"$top\", \"1\")\n};\n\nvar graphResult = graphClient.Users.Request(options).GetAsync().Result;\nConsole.WriteLine(\"Graph SDK Result\");\nConsole.WriteLine(graphResult[0].DisplayName);<\/pre>\n<p>Continuing in the\u00a0<strong>Main<\/strong>\u00a0method add the following to get an authenticated instance of the\u00a0<strong>HttpClient<\/strong>\u00a0and send a request to retrieve the first user from &#8220;\/Users&#8221; endpoint on the Microsoft Graph.<\/p>\n<pre style=\"padding-left: 30px\">\/\/Direct query using HTTPClient (for beta endpoint calls or not available in Graph SDK)\nHttpClient httpClient = GetAuthenticatedHTTPClient(config);\nUri Uri = new Uri(\"https:\/\/graph.microsoft.com\/v1.0\/users?$top=1\");\nvar httpResult = httpClient.GetStringAsync(Uri).Result;\n\nConsole.WriteLine(\"HTTP Result\");\nConsole.WriteLine(httpResult);<\/pre>\n<p>&nbsp;<\/p>\n<p>This completes all file edits and additions. Ensure all files are saved. In order to test the console application, run the following commands from the command line:<\/p>\n<pre style=\"padding-left: 30px\">dotnet build\ndotnet run<\/pre>\n<p>Consider what this code is doing.<\/p>\n<ul>\n<li>The\u00a0<strong>GetAuthenticatedGraphClient<\/strong>\u00a0function initializes a\u00a0<strong>GraphServiceClient<\/strong>\u00a0with an authentication provider that calls\u00a0<strong>AcquireTokenForClientAsync<\/strong>.<\/li>\n<li>In the\u00a0<strong>Main<\/strong>\u00a0function:\n<ul>\n<li>The graph endpoint that will be called is\u00a0\/v1.0\/users\/$top=1.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>Try It Out<\/h2>\n<p>Create (or re-use) your own Azure AD application from the preview Azure AD app registration portal using <a href=\"https:\/\/developer.microsoft.com\/en-us\/graph\/blogs\/30daysmsgraph-day-9-azure-ad-applications-on-v2-endpoint\">Day 9<\/a> instructions.\u00a0 Then do one (or both if you are ambitious) of the following:<\/p>\n<p><a href=\"https:\/\/github.com\/microsoftgraph\/30DaysMSGraph-TryItOut\/blob\/master\/Day15-DotnetCoreSample.md\">Day 15 repo link<\/a><\/p>\n<ol>\n<li>Build out the above sample project from an empty .NET Core console application.<\/li>\n<li>Clone the official <a href=\"https:\/\/github.com\/microsoftgraph\/dotnetcore-console-sample\">.NET Core console sample<\/a> from Microsoft Graph repo and configure it to use your Azure AD app registration.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>Join us tomorrow as we start a series of use case demos extending the .NET Core console application from today to make Microsoft Graph requests against multiple endpoints. First up will be creating a new user by calling the Azure AD endpoint in <a href=\"https:\/\/developer.microsoft.com\/en-us\/graph\/blogs\/30daysmsgraph-day-16-use-case-create-user-in-azure-ad\">Day 16<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Day 14 we discussed batch processing requests to Microsoft Graph requests.\u00a0 Today we&#8217;ll take a big jump and build our first project in .Net Core that can make calls against Microsoft Graph.<\/p>\n","protected":false},"author":73055,"featured_media":25159,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[84],"class_list":["post-2058","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-microsoft-graph","tag-30daysmsgraph"],"acf":[],"blog_post_summary":"<p>In Day 14 we discussed batch processing requests to Microsoft Graph requests.\u00a0 Today we&#8217;ll take a big jump and build our first project in .Net Core that can make calls against Microsoft Graph.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/2058","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/users\/73055"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/comments?post=2058"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/2058\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media\/25159"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media?parent=2058"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/categories?post=2058"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/tags?post=2058"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}