Når man arbejder med webapplikationer, kan det være nødvendigt at reducere antallet af serveranmodninger, især når data ikke ændrer sig hyppigt. En måde at opnå dette på er ved at implementere caching. I Blazor, som bygger på WebAssembly, er der flere metoder til at cache data effektivt. En af de mest populære metoder involverer brugen af browserens lokale lagring, som tillader data at blive gemt på tværs af flere sessioner og tabs. I denne artikel gennemgår vi, hvordan du kan implementere caching ved at bruge lokal lagring til at cache en liste af medarbejdere i en Blazor-applikation.

Når man ser på hvordan data som medarbejderinformation bliver indlæst i en Blazor-applikation, kan vi bruge QuickGrid-komponenten til at vise disse data på en effektiv måde. QuickGrid giver os mulighed for at sortere data uden at skulle hente dem igen fra serveren. Dette er nyttigt, når vi arbejder med store datamængder, men vi skal stadig være opmærksomme på at undgå unødvendige serveranmodninger, især hvis dataene ikke ændrer sig hyppigt.

Når vi ser på det grundlæggende i Blazor, opbevares data i hukommelsen, og hver gang vi navigerer til en ny side, skal vi hente dataene fra serveren igen. Dette kan være ineffektivt, især hvis brugeren navigerer frem og tilbage mellem siderne. Her kommer lokal lagring ind i billedet. Lokal lagring er tilgængelig på tværs af tabs og sessioner og kan gemme store mængder data uden at påføre serveren unødvendig belastning.

Blazor understøtter ikke direkte adgang til browserens lokale lagring, så vi skal bruge JavaScript interop for at kommunikere med browserens storage API. JavaScript interop gør det muligt at udføre operationer som at gemme, hente og fjerne data fra lokal lagring. For at gøre denne integration lettere for Blazor-udviklere, kan vi oprette et JavaScript-modul, der indeholder funktioner til at arbejde med lokal lagring, og derefter interagere med det via Blazor.

Når vi opretter en interop mellem Blazor og JavaScript, bruger vi IJSRuntime interface, som gør det muligt at importere JavaScript-moduler dynamisk. Dette betyder, at vi kan udskyde indlæsningen af JavaScript-modulet, indtil det er nødvendigt, hvilket sparer ressourcer og sikrer, at JavaScript-moduler kun indlæses, når de rent faktisk skal bruges.

For at implementere lokal lagring i vores applikation, skal vi først oprette et JavaScript-modul, der indeholder funktionerne til at arbejde med lokal lagring. Dette modul kan se sådan ud:

javascript
export function get(key) {
return window.localStorage.getItem(key); } export function set(key, value) { window.localStorage.setItem(key, value); } export function clear() { window.localStorage.clear(); } export function remove(key) { window.localStorage.removeItem(key); }

Dette modul giver os de nødvendige funktioner til at hente, gemme og fjerne data i lokal lagring. Når vi har oprettet dette modul, kan vi bruge Blazor's interop til at kommunikere med det.

I Blazor-appen skal vi derefter oprette en service, der bruger IJSRuntime til at interagere med JavaScript-modulet. Denne service kan se sådan ud:

csharp
public class LocalStorageService : IAsyncDisposable
{ private readonly IJSRuntime jsRuntime; private Lazy<IJSObjectReference> jsModule = new(); public LocalStorageService(IJSRuntime jsRuntime) { this.jsRuntime = jsRuntime; } private async Task WaitForReference() { if (!jsModule.IsValueCreated) { jsModule = new(await jsRuntime.InvokeAsync<IJSObjectReference>("import", "/js/LocalStorageInterop.js")); } } public async Task SetValueAsync(string key, string value) { await WaitForReference(); await jsModule.Value.InvokeVoidAsync("set", key, value); } public async Task<string> GetValueAsync(string key) { await WaitForReference();
return await jsModule.Value.InvokeAsync<string>("get", key);
}
public async Task ClearAsync() { await WaitForReference(); await jsModule.Value.InvokeVoidAsync("clear"); } public async Task RemoveAsync(string key) { await WaitForReference(); await jsModule.Value.InvokeVoidAsync("remove", key); } public async ValueTask DisposeAsync() { if (jsModule.IsValueCreated) { await jsModule.Value.DisposeAsync(); } } }

Denne service sørger for, at vi kun indlæser JavaScript-modulet én gang og gemmer det i en lazy reference. Dette gør applikationen mere effektiv, da vi undgår at indlæse modulet flere gange. Service-metoderne som SetValueAsync og GetValueAsync anvender JavaScript interop til at gemme og hente data fra lokal lagring.

Efter at have oprettet denne service, skal vi tilmelde den i Program.cs som en scoped service, hvilket sikrer, at en enkelt instans af servicen bliver oprettet for hver HTTP-anmodning. Dette kan gøres med følgende kode:

csharp
builder.Services.AddScoped<LocalStorageService>();

Nu kan vi begynde at bruge lokal lagring til at cache data i vores Blazor-komponenter. For eksempel kan vi oprette en komponent, der henter medarbejderdata, og vi kan gemme disse data i lokal lagring i et specifikt tidsinterval. Hvis dataene allerede er cachet og ikke er udløbet, bruger komponenten de cachede data i stedet for at lave en ny serveranmodning.

csharp
@inject LocalStorageService localStorage @code {
private const string keyTS = "employeesLastGet";
private const string keyData = "employeesArray";
[Parameter] public TimeSpan CacheDuration { get; set; } = TimeSpan.FromMinutes(10);
private IQueryable<Employee> employees; protected override async Task OnParametersSetAsync() { string employeesJson = await localStorage.GetValueAsync(keyData); DateTime lastGet = DateTime.Parse(await localStorage.GetValueAsync(keyTS)); if (employeesJson != "{}" && DateTime.Now - lastGet < CacheDuration) { employees = JsonSerializer.Deserialize<IQueryable<Employee>>(employeesJson); } else { // Hent medarbejdere fra webservice og cache dem var result = await GetEmployeesFromService(); employeesJson = JsonSerializer.Serialize(result); await localStorage.SetValueAsync(keyData, employeesJson); await localStorage.SetValueAsync(keyTS, DateTime.Now.ToString()); employees = result.AsQueryable(); } } }

Denne komponent tjekker først, om cachede data eksisterer og om de stadig er gyldige baseret på cachetiden. Hvis de er, bruges de cachede data, ellers bliver der sendt en anmodning til serveren og de nye data gemmes i lokal lagring.

Ved at implementere caching med lokal lagring reducerer vi antallet af anmodninger til serveren, hvilket gør applikationen hurtigere og mere effektiv. Denne tilgang kan også anvendes til at muliggøre offline-funktionalitet, hvilket kan være nyttigt i applikationer, hvor netværksforbindelsen ikke altid er stabil.

Hvordan man bygger og sikrer webtjenester med Minimal APIs

For at sikre, at din webtjeneste fungerer korrekt, skal du først og fremmest bygge dit projekt. Hvis du ikke har gennemført opgaven om at oprette klassebibliotekerne i Kapitel 2, kan du hente løsningsprojekterne fra GitHub-repositoriet. Når du er klar, skal du bruge kommandoen på terminalen til at bygge projektet:

nginx
dotnet build

Dette sikrer, at klassebibliotekerne, som ikke er en del af den aktuelle løsning, bliver korrekt kompilerede. Efter at have bygget projektet skal du konfigurere din webtjeneste korrekt. Gå til mappen Properties og åbn filen launchSettings.json. Her skal du ændre portnummeret for den HTTPS-profil, så det bruger port 5091. Dette sikrer, at din webtjeneste kan tilgås via HTTPS, hvilket er vigtigt for både sikkerheden og udviklingsarbejdet:

json
"profiles": { "https": { "commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true, "launchUrl": "swagger", "applicationUrl": "https://localhost:5091", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } }

Når du har justeret din launch-konfiguration, skal du åbne filen Program.cs og fjerne eventuelle ubrugte service-konfigurationer, såsom vejrtjenesten, og erstatte dem med konfigurationer for at håndtere de forskellige HTTP-anmodninger. Dette gøres ved at definere, hvordan hver API-rute skal reagere. Eksemplet nedenfor viser, hvordan du opretter en minimal API, der håndterer forskellige ruter for produkter i en database:

csharp
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.OpenApi; using Packt.Shared; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddNorthwindContext(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); int pageSize = 10; app.MapGet("api/products", ([FromServices] NorthwindContext db, [FromQuery] int? page) => db.Products.Where(product => product.UnitsInStock > 0 && !product.Discontinued) .Skip(((page ?? 1) - 1) * pageSize) .Take(pageSize)) .WithName("GetProducts") .WithOpenApi(operation => { operation.Description = "Hent produkter med UnitsInStock > 0 og Discontinued = false."; operation.Summary = "Hent produkter på lager, som ikke er udgået."; return operation; }) .Produces(StatusCodes.Status200OK); app.Run();

I dette kodeeksempel er der konfigureret ruter til at hente produkter, der er på lager og ikke udgået, ved hjælp af en GET-forespørgsel. Hver rute kan have sin egen beskrivelse og opsummering for at forbedre API-dokumentationen, som kan vises i Swagger UI.

For at sikre, at webtjenesten fungerer korrekt, kan du bruge Swagger UI til at teste dine API-ruter. Når du kører webtjenesten, kan du navigere til https://localhost:5091/swagger for at se dokumentationen og teste dine endepunkter. For eksempel kan du teste GET /api/products og angive en query parameter som page for at hente produkter i pagineret form. Dette gør det lettere at navigere i store mængder data.

Når du har testet grundlæggende ruter som GET /api/products, kan du også teste ruter som GET /api/products/{id} for at hente detaljer om et specifikt produkt, eller POST /api/products for at oprette nye produkter i databasen. For mere avanceret funktionalitet kan du også håndtere opdatering af eksisterende produkter med PUT-metoden og sletning af produkter med DELETE.

Husk, at brug af HTTPS er en vigtig sikkerhedsforanstaltning, når du arbejder med webtjenester. Ved at sikre din API med HTTPS sikrer du, at dataene, der sendes mellem klienten og serveren, forbliver krypteret og beskyttet mod potentielle angreb.

Når du har konfigureret og testet din webtjeneste, kan du begynde at bruge værktøjer som Visual Studio Code og dets REST Client-extension til at foretage mere avancerede tests. REST Client giver dig mulighed for at sende HTTP-anmodninger direkte fra editoren og hurtigt få svar fra din webtjeneste uden at skulle åbne en webbrowser.

Det er vigtigt at forstå, at hver rute i din API kan have en unik beskrivelse og opsummering. Dette gør det lettere at integrere med andre udviklere og sikre, at brugerne af API'et ved præcist, hvad hver rute gør. Desuden bør du sørge for, at alle metoder (GET, POST, PUT, DELETE) er korrekt dokumenterede og implementeret.

Som en vigtig bemærkning bør du også sikre, at din API håndterer fejl korrekt. Det betyder, at du bør returnere passende HTTP-statuskoder, såsom 404 Not Found eller 500 Internal Server Error, når der opstår problemer. Dette giver et bedre brugeroplevelse og hjælper udviklere med hurtigt at forstå, hvad der gik galt.