-
Notifications
You must be signed in to change notification settings - Fork 0
Lab 5.2 ‐ Move Pages Clientside
Now that we have a WebAPI serving our data and have authentication and authorization in place, we can further improve our application's architecture.
Let's move the rendering of our pages to the browser, and reduce the load on the server.
The process of moving our pages is pretty much the same for all pages. We'll take the AddRoom.razor
page as an example.
If you want your page to render in the browser, you have to set its @rendermode
to InteractiveWebAssembly
. Once you do that, you have to move the page to the HmsBlazor.Client
project, because it must be included in the client bundle that is downloaded by the browser.
- Move the
AddRoom.razor
page to thePages
folder in theHmsBlazor.Client
project - Change its
@rendermode
toInteractiveWebAssembly
Your page will now be rendered in the browser.
You will now find that the AddRoom.razor
page causes compilation errors. That is because the Room
entity is not known client side. The easiest way to solve that is to move the Entities
folder from the HmsBlazor
(server side) project to the HmsBlazor.Client
project. Now, these classes will be available both client and server side.
Now, when you test your app and navigate to the /addroom
page, you will notice an error. In the browser's error console (press F12
in your browser to reveal the Developer Tools), you will find an error that says:
Cannot provide a value for property 'Http' on type 'HmsBlazor.Client.Pages.AddRoom'. There is no registered service of type 'System.Net.Http.HttpClient'
We'll fix this in the next step.
Now that your page lives in the browser, it has a different runtime: the Blazor WebAssembly runtime. The services you registered for Dependency Injection in the HmsBlazor
server project don't exist here, so you will also have to register them in the client.
- Add the
Microsoft.Extensions.Http
Nuget package theHmsBlazor.Client.csproj
inside the<PackageReferences>
node:
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
- Open the
Program.cs
of theHmsBlazor.Client
project and add the following code:
builder.Services.AddHttpClient("HmsApi", (sp, client) =>{
var navigation = sp.GetRequiredService<NavigationManager>();
client.BaseAddress = new Uri(navigation.BaseUri);
});
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("HmsApi"));
This will setup an HttpClient
configured for calling the HMS WebAPI. On the client side, we can use the NavigationManager
to resolve the base address the page is running on, so we don't have to put anything in the appsettings.json
.
Note that even though the code looks very similar, not all services or types are always available both on the server side and in WebAssembly.
- Test your application again; the
/addroom
page should work again.
Since we added authentication and authorization to our Blazor app using EntraID, we can now also secure our WebAPI.
- In the
RoomController
in theHmsBlazor
server project, add an[Authorize]
attribute above the class definition:
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class RoomController ...
For more specific authorization, add an attribute above the AddRoom
, UpdateRoom
and DeleteRoom
methods:
[Authorize(Roles="Role.Owner")]
This will instruct the ASP.NET authorization middleware to only allow authenticated users that have the Role.Owner
role in their claimset. This means that the hms_manager
user will not be able to call the APIs to manage rooms. The API call will be redirected to the AccessDenied
page we created earlier.
Since we have authentication setup in our Blazor application, and the Web API lives on the same URL / base address as the web app, the authentication middleware will send authentication cookies to the server on each request. This way, we have transparent and out-of-the box authentication, and we can identify the user on the server.
For completeness, add an [Authorize]
attribute on all API controller classes. Now, we also have to tweak the HttpClient
that lives on the server side a bit. In Program.cs
, find the registration of the HttpCLient
and replace it with the following:
// enables HttpClientFactory.CreateClient()
builder.Services.AddHttpClient("HmsApi",
client => client.BaseAddress = new Uri(baseAddress))
.AddMicrosoftIdentityAppAuthenticationHandler("HmsApi", entraConfig);
The addition of .AddMicrosoftIdentityAppAuthenticationHandler("HmsApi", entraConfig)
will inject a DelegatingHandler
into the HttpClient
which will make it fetch an access token for the API before executing the request. It uses the values from the EntraID
config section for authentication. The ClientSecret
you configured earlier in this lab is important for this!
Finally, nove all pages from the server to the client project, EXCEPT Reservation.razor
!
Make sure to change the @rendermode
to ``InteractiveWebAssembly`:
@rendermode=InteractiveServer
becomes:
@rendermode=InteractiveWebAssembly
Now all pages will work again and will be able to call the API from the client side using authentication. In addition Reservation.razor
should be able to call the APIs from the server side.
⚠️ Do not moveReservation.razor
to the client project. We have some interesting plans for that in the next lab, which requires code to run on the server.