|
| 1 | +--- |
| 2 | +title: "Quickstart: Blazor Server Web API sample (C#) (Common Data Service)| Microsoft Docs" |
| 3 | +description: "This sample demonstrates how to authenticate with a Common Data Service from a Blazor Server application and then call a basic WhoAmI Web API function." |
| 4 | +ms.custom: "" |
| 5 | +ms.date: 07/07/2020 |
| 6 | +ms.service: powerapps |
| 7 | +ms.topic: "article" |
| 8 | +author: "JeremyLikness" # GitHub ID |
| 9 | +ms.author: "jeliknes" # MSFT alias of Microsoft employees only |
| 10 | +ms.reviewer: "pehecke" |
| 11 | +manager: "" # MSFT alias of manager or PM counterpart |
| 12 | +search.audienceType: |
| 13 | + - developer |
| 14 | +search.app: |
| 15 | + - PowerApps |
| 16 | + - D365CE |
| 17 | +--- |
| 18 | +# Quickstart: Blazor Server Web API sample (C#) |
| 19 | + |
| 20 | +In this quickstart, you'll create a Blazor Server application to connect to your Common Data Service environment using the Web API. |
| 21 | + |
| 22 | +You'll authenticate and use <xref:System.Net.Http.HttpClient> to send a `GET` request containing the [WhoAmI](/dynamics365/customer-engagement/web-api/whoami) function. The response will be a [WhoAmIResponse](/dynamics365/customer-engagement/web-api/whoamiresponse) complex type. After call completion, the `UserId` property value is displayed. |
| 23 | + |
| 24 | +> [!NOTE] |
| 25 | +> This is a very simple example to show how to get connected with a minimum of code. The [Enhanced quickstart](enhanced-quick-start.md) will build upon this sample to apply better design patterns. |
| 26 | +
|
| 27 | +## Prerequisites |
| 28 | + |
| 29 | +- Visual Studio 2019 (version 16.6.2 or later recommended) |
| 30 | +- Familiarity with the Microsoft Azure portal |
| 31 | +- Internet connection |
| 32 | +- Valid user account for a Common Data Service instance |
| 33 | +- Administrator access to grant application registrations |
| 34 | +- URL to the Common Data Service environment you want to connect with |
| 35 | +- Basic understanding of the Visual C# language |
| 36 | + |
| 37 | +> [!NOTE] |
| 38 | +> To authenticate you must have an app registered in Azure Active Directory. The registration will happen automatically as part of the template creation, but will require additional updates in the Azure portal. |
| 39 | +
|
| 40 | +## Create a Visual Studio project |
| 41 | + |
| 42 | +1. Create a new Blazor Server app using .NET Core 3.1 but don't choose **Create** just yet. |
| 43 | + |
| 44 | +  |
| 45 | + |
| 46 | +1. Select **Change** under **Authentication** and then choose **Work or School Accounts**. |
| 47 | + |
| 48 | +  |
| 49 | + |
| 50 | +1. Choose the appropriate dropdown and then replace `CRM520451` in the example with your environment's name. |
| 51 | + |
| 52 | +1. Select **Create**. |
| 53 | + |
| 54 | +## Configure the application in Active Directory |
| 55 | + |
| 56 | +By default, the template will create a registered application. Connecting to Common Data Service will require additional permissions. Open the Azure portal and log in with your credentials. Navigate to **Active Directory** and **App Registrations**, and then choose the entry with the same name as your application. |
| 57 | + |
| 58 | +1. Choose **Authentication**, select (check) **Access tokens** under **Implicit grant**, and then click **Save**. |
| 59 | + |
| 60 | +  |
| 61 | + |
| 62 | +1. Choose **Certificates & secrets** and then select **New client secret**. |
| 63 | + |
| 64 | +1. Assign the secret a name (for example, "Blazor Server client") and expiration date, and then select **Add**. |
| 65 | + |
| 66 | +1. Select the clipboard icon next to your secret to copy it. |
| 67 | + |
| 68 | +  |
| 69 | + |
| 70 | +1. In your Blazor Server app, open `appsettings.json` and add an entry for "ClientSecret". The Active Directory settings should look like this: |
| 71 | + |
| 72 | + ```json |
| 73 | + { |
| 74 | + "AzureAd": { |
| 75 | + "Instance": "https://login.microsoftonline.com/", |
| 76 | + "Domain": "{org}.onmicrosoft.com", |
| 77 | + "TenantId": "{tenantId}", |
| 78 | + "ClientId": "{clientId}", |
| 79 | + "ClientSecret": "{secret}", |
| 80 | + "CallbackPath": "/signin-oidc" |
| 81 | + } |
| 82 | + } |
| 83 | + ``` |
| 84 | + |
| 85 | +1. Navigate to **API permissions** |
| 86 | + |
| 87 | +1. Select **Add a permission** and choose **Dynamics CRM** |
| 88 | + |
| 89 | +1. Choose **Delegated permissions** and select (check) **user_impersonation**, and then click **Add permissions** |
| 90 | + |
| 91 | +  |
| 92 | + |
| 93 | +1. Select the newly created permission to highlight it, and then shoose **Grant admin consent for organization** (your environment name is shown) |
| 94 | + |
| 95 | +1. Verify the permissions have green checkboxes in the **status** column |
| 96 | + |
| 97 | +## Prepare the app to use Azure AD tokens |
| 98 | + |
| 99 | +The application requires some extra steps to capture the authentication token and pass it to the Web API request. |
| 100 | + |
| 101 | +1. Right-click on the **Data** folder and add a new class named `TokenProvider`. |
| 102 | + |
| 103 | + ```csharp |
| 104 | + public class TokenProvider |
| 105 | + { |
| 106 | + public string AccessToken { get; set; } |
| 107 | + } |
| 108 | + ``` |
| 109 | + |
| 110 | +1. Open the App.razor file and add the following statements to the top of the file. Change the namespace to match the name of your application. |
| 111 | + |
| 112 | + ```razor |
| 113 | + @using BlazorWebAPIExample.Data |
| 114 | + @inject TokenProvider Service |
| 115 | + ``` |
| 116 | + |
| 117 | +1. Add a `@code` block to accept a parameter and move the token into the service. |
| 118 | + |
| 119 | + ```csharp |
| 120 | + [Parameter] |
| 121 | + public TokenProvider InitialState { get; set; } |
| 122 | + |
| 123 | + protected override void OnInitialized() |
| 124 | + { |
| 125 | + Service.AccessToken = InitialState.AccessToken; |
| 126 | + base.OnInitialized(); |
| 127 | + } |
| 128 | + ``` |
| 129 | + |
| 130 | +1. Open the Pages/\_Host.cshtml file and add the following using statements after the namespace declaration. |
| 131 | + |
| 132 | + ```razor |
| 133 | + @using BlazorCommonDataService.Data |
| 134 | + @using Microsoft.AspNetCore.Authentication |
| 135 | + ``` |
| 136 | + |
| 137 | +1. After the `<body>` tag, add the following code and update the app component to acquire and pass the token. |
| 138 | + |
| 139 | + ```razor |
| 140 | + @{ |
| 141 | + var token = new TokenProvider |
| 142 | + { |
| 143 | + AccessToken = await HttpContext.GetTokenAsync("access_token") |
| 144 | + }; |
| 145 | + } |
| 146 | + <app> |
| 147 | + <component type="typeof(App)" param-InitialState="token" render-mode="ServerPrerendered" /> |
| 148 | + </app> |
| 149 | + ``` |
| 150 | + |
| 151 | +1. Obtain the environment name for the Common Data Services management API. If you're not sure what the name is, open the [Power Platform admin center](https://admin.powerplatform.microsoft.com/environments), navigate to **Environments** then choose **Open environment**. You will see a URL like this: `https://{org}.crm.dynamics.com` where {org} is the environment name. |
| 152 | + |
| 153 | +1. Add an entry named `CDSAPI` to the appsettings.json file with the environment URL as the value. Append `/api/data/v.9.0/` to the end of the URL so it looks like this: |
| 154 | + |
| 155 | + ```json |
| 156 | + { "CDSAPI": "https://{org}.crm.dynamics.com/api/data/v9.0/" } |
| 157 | + ``` |
| 158 | + |
| 159 | +1. Add this `using` statement to the file Startup.cs. |
| 160 | + |
| 161 | + ```csharp |
| 162 | + using Microsoft.AspNetCore.Authentication.OpenIdConnect; |
| 163 | + ``` |
| 164 | + |
| 165 | +1. In the `Startup.cs` class, add registrations to retrieve the authentication token and configure a client ready to use the token. Place this code between `services.AddAuthentication` and `services.AddControllersWithViews`. |
| 166 | + |
| 167 | + ```csharp |
| 168 | + services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, |
| 169 | + opt => |
| 170 | + { |
| 171 | + var resourceUri = new Uri(Configuration["CDSAPI"]); |
| 172 | + var resource = $"{resourceUri.Scheme}://{resourceUri.Host}/"; |
| 173 | + opt.ResponseType = "code"; |
| 174 | + opt.SaveTokens = true; |
| 175 | + opt.Scope.Add("user_impersonation"); |
| 176 | + opt.Scope.Add(new Uri(Configuration["CDSAPI"]).Host); |
| 177 | + opt.Resource = resource; |
| 178 | + }); |
| 179 | + services.AddScoped<TokenProvider>(); |
| 180 | + services.AddHttpClient("CDS", client => |
| 181 | + { |
| 182 | + client.BaseAddress = new Uri(Configuration["CDSAPI"]); |
| 183 | + }); |
| 184 | + ``` |
| 185 | + |
| 186 | +The first registration allows requesting the token with the proper scope. The second registers the service that tracks the token, and the third creates a client with the base API address pre-configured. |
| 187 | + |
| 188 | +## Make a call to the Web API |
| 189 | + |
| 190 | +Next, you'll update the `Index.razor` component to call the Web API. |
| 191 | + |
| 192 | +1. Open the Index.razor file and add these statements to the top: |
| 193 | + |
| 194 | + ```razor |
| 195 | + @using System.Text.Json |
| 196 | + @using BlazorWebAPIExample.Data; |
| 197 | + @using System.Net.Http.Headers; |
| 198 | + @inject IHttpClientFactory Factory |
| 199 | + @inject TokenProvider TokenProvider |
| 200 | + ``` |
| 201 | + |
| 202 | +1. Add this markup after the `SurveyPrompt` component: |
| 203 | + |
| 204 | + ```razor |
| 205 | + @if (Loading) |
| 206 | + { |
| 207 | + <div class="alert alert-warning">Loading...</div> |
| 208 | + } |
| 209 | + @if (Error) |
| 210 | + { |
| 211 | + <div class="alert alert-danger">@ErrorMessage</div> |
| 212 | + } |
| 213 | + @if (!Loading && !Error) |
| 214 | + { |
| 215 | + <div class="alert alert-info">You did it! Your user id is: @UserId</div> |
| 216 | + } |
| 217 | + ``` |
| 218 | + |
| 219 | +1. Finally, add a `@code` block with the following code: |
| 220 | + |
| 221 | + ```csharp |
| 222 | + public bool Loading; |
| 223 | + public string ErrorMessage; |
| 224 | + public bool Error => !string.IsNullOrWhiteSpace(ErrorMessage); |
| 225 | + public string UserId; |
| 226 | + |
| 227 | + protected override async Task OnInitializedAsync() |
| 228 | + { |
| 229 | + Loading = true; |
| 230 | + try |
| 231 | + { |
| 232 | + var client = Factory.CreateClient("CDS"); |
| 233 | + client.DefaultRequestHeaders.Authorization = |
| 234 | + new AuthenticationHeaderValue("Bearer", TokenProvider.AccessToken); |
| 235 | + var result = await client.GetAsync("WhoAmI"); |
| 236 | + result.EnsureSuccessStatusCode(); |
| 237 | + UserId = JsonDocument.Parse(await result.Content.ReadAsStringAsync()) |
| 238 | + .RootElement.GetProperty("UserId").GetGuid().ToString(); |
| 239 | + } |
| 240 | + catch (Exception ex) |
| 241 | + { |
| 242 | + ErrorMessage = ex.Message; |
| 243 | + } |
| 244 | + finally |
| 245 | + { |
| 246 | + Loading = false; |
| 247 | + } |
| 248 | + await base.OnInitializedAsync(); |
| 249 | + } |
| 250 | + ``` |
| 251 | + |
| 252 | +The application is now ready! |
| 253 | + |
| 254 | +## Run the program |
| 255 | + |
| 256 | +Press F5 to run the program. The output should look like this: |
| 257 | + |
| 258 | + |
| 259 | + |
| 260 | +**Congratulations!** You have successfully connected to the Web API. |
| 261 | + |
| 262 | +This quickstart sample shows a simple approach to create a Visual Studio project without any exception handling or method to refresh the access token. You can expand on the example to perform more complex operations, and wrap the `HttpClient` object in a service class to handle the permissions. |
| 263 | + |
| 264 | +The [Enhanced quickstart](enhanced-quick-start.md) topic shows how to: |
| 265 | + |
| 266 | +- Implement exception handling methods |
| 267 | +- Use basic authentication with a connection string |
| 268 | +- Create a reusable method to refresh the access token |
| 269 | +- Build reusable methods for data operations |
| 270 | + |
| 271 | +## Next steps |
| 272 | + |
| 273 | +Learn how to structure your code for a better design. |
| 274 | + |
| 275 | +> [!div class="nextstepaction"] |
| 276 | +> [Enhanced quickstart](enhanced-quick-start.md)<br/> |
| 277 | + |
| 278 | +### See Also |
| 279 | + |
| 280 | +[Tutorial: Create an ASP.NET Core Blazor WebAssembly App using Common Data Service](../walkthrough-blazor-webassembly-single-tenant.md) |
0 commit comments