Skip to content

Commit 690a5f6

Browse files
authored
Merge pull request MicrosoftDocs#3049 from JeremyLikness/jrl-cds-webapi-update
Blazor Server CDS Quickstart
2 parents 746b74c + 72e8bd5 commit 690a5f6

8 files changed

+282
-0
lines changed
96.9 KB
Loading
56.7 KB
Loading
64.9 KB
Loading
46.6 KB
Loading
78.7 KB
Loading
70.9 KB
Loading

powerapps-docs/developer/common-data-service/webapi/TOC.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
href: get-started-dynamics-365-web-api-csharp.md
1111
- name: 'Quick Start: Web API sample (C#)'
1212
href: quick-start-console-app-csharp.md
13+
- name: 'Quick Start: Blazor Server sample (C#)'
14+
href: quick-start-blazor-server-app.md
1315
- name: Enhanced quick start (C#)
1416
href: enhanced-quick-start.md
1517
- name: Start Web API project in Visual Studio
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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+
![Start a Blazor Server project](../media/quick-start-blazor-server-app-csharp-1.png)
45+
46+
1. Select **Change** under **Authentication** and then choose **Work or School Accounts**.
47+
48+
![Choose authentication](../media/quick-start-blazor-server-app-csharp-2.png)
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+
![Implicit grant](../media/quick-start-blazor-server-app-csharp-3.png)
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+
![Copy secret](../media/quick-start-blazor-server-app-csharp-4.png)
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+
![Add permission](../media/quick-start-blazor-server-app-csharp-5.png)
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+
![Connection success](../media/quick-start-blazor-server-app-csharp-6.png)
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

Comments
 (0)