Del 7 av 8

Web API-anrop & auth-grunder

Hur en Blazor-app pratar med ett HTTP API via HttpClient och hur du skyddar sidor med Blazors inbyggda autentiseringsstöd.

Lektion 7 — V7. Vi tar steget från lokal data till riktig integration: GET/POST/PUT/DELETE mot ett API, felhantering, och en första titt på AuthorizeView.

HttpClient — registrering

// Program.cs
builder.Services.AddHttpClient<ProductApi>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Api:BaseUrl"]!);
});

// För Blazor Server kan du också använda en generisk:
builder.Services.AddHttpClient();

En typad API-klient

public class ProductApi
{
    private readonly HttpClient _http;
    public ProductApi(HttpClient http) => _http = http;

    public async Task<List<Product>> GetAllAsync()
        => await _http.GetFromJsonAsync<List<Product>>("products") ?? new();

    public async Task<Product?> GetAsync(int id)
        => await _http.GetFromJsonAsync<Product>($"products/{id}");

    public async Task<Product> CreateAsync(Product p)
    {
        var resp = await _http.PostAsJsonAsync("products", p);
        resp.EnsureSuccessStatusCode();
        return (await resp.Content.ReadFromJsonAsync<Product>())!;
    }

    public async Task UpdateAsync(int id, Product p)
    {
        var resp = await _http.PutAsJsonAsync($"products/{id}", p);
        resp.EnsureSuccessStatusCode();
    }

    public async Task DeleteAsync(int id)
    {
        var resp = await _http.DeleteAsync($"products/{id}");
        resp.EnsureSuccessStatusCode();
    }
}

Använda klienten i en komponent

@page "/products"
@inject ProductApi Api

<h1>Produkter</h1>

@if (loading)
{
    <p>Laddar...</p>
}
else if (error != null)
{
    <div class="alert">@error</div>
}
else
{
    <ul>
        @foreach (var p in products)
        {
            <li>@p.Name — @p.Price.ToString("C")</li>
        }
    </ul>
}

@code {
    private List<Product> products = new();
    private bool loading = true;
    private string? error;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            products = await Api.GetAllAsync();
        }
        catch (HttpRequestException ex)
        {
            error = $"Kunde inte hämta produkter: {ex.Message}";
        }
        finally
        {
            loading = false;
        }
    }
}
System.Net.Http.Json Extension-metoderna GetFromJsonAsync, PostAsJsonAsync, PutAsJsonAsync och ReadFromJsonAsync finns i namespace System.Net.Http.Json. De använder System.Text.Json under huven.

CancellationToken — avbryt långa anrop

private CancellationTokenSource? _cts;

protected override async Task OnInitializedAsync()
{
    _cts = new CancellationTokenSource();
    try
    {
        products = await Api.GetAllAsync(_cts.Token);
    }
    catch (OperationCanceledException) { /* användaren navigerade bort */ }
}

public void Dispose() => _cts?.Cancel();

Felhantering på app-nivå

Blazor har ett inbyggt <ErrorBoundary> som fångar undantag från sina barn:

<ErrorBoundary>
    <ChildContent>
        <ProductList />
    </ChildContent>
    <ErrorContent Context="ex">
        <div class="alert alert-danger">
            <p>Något gick fel: @ex.Message</p>
        </div>
    </ErrorContent>
</ErrorBoundary>

Auth — översikt

Blazor använder ASP.NET Cores standard-autentisering. Den vanligaste vägen i en ny app är att skapa projektet med --auth Individual:

dotnet new blazor -o MinApp --interactivity Server --auth Individual

Detta ger dig:

AuthorizeView — visa/dölj beroende på inloggning

<AuthorizeView>
    <Authorized>
        <p>Hej, @context.User.Identity?.Name!</p>
        <a href="Account/Logout">Logga ut</a>
    </Authorized>
    <NotAuthorized>
        <a href="Account/Login">Logga in</a>
    </NotAuthorized>
</AuthorizeView>

<AuthorizeView Roles="Admin">
    <a href="/admin">⚙️ Admin</a>
</AuthorizeView>

Skydda en hel sida

@page "/admin/dashboard"
@attribute [Authorize(Roles = "Admin")]

<h1>Admin-dashboard</h1>

AuthenticationStateProvider — användarinfo i kod

@inject AuthenticationStateProvider AuthState

@code {
    protected override async Task OnInitializedAsync()
    {
        var state = await AuthState.GetAuthenticationStateAsync();
        var user  = state.User;
        if (user.Identity?.IsAuthenticated == true)
        {
            var name  = user.Identity.Name;
            var roles = user.Claims
                .Where(c => c.Type == ClaimTypes.Role)
                .Select(c => c.Value)
                .ToList();
        }
    }
}
Säkerhetspåminnelse UI-skydd med AuthorizeView hindrar bara visning. Skydda alltid API-endpoints med [Authorize] på servern också. Klientvalidering är aldrig säkerhet.

.NET 10: Auth- och HTTP-förbättringar

.NET 10 har flera målmedvetna förbättringar för säkerhet och HTTP — viktigast för dig som bygger Blazor-appar:

Cookie-auth: 401/403 för API-endpoints Tidigare returnerade cookie-baserad autentisering en 302-redirect till login-sidan även för API-anrop — vilket fick fetch/HttpClient att få tillbaka HTML i stället för JSON. I .NET 10 returneras nu 401 Unauthorized (saknar inloggning) respektive 403 Forbidden (saknar behörighet) för endpoints som markerats som API. Du behöver inte längre skriva en egen OnRedirectToLogin-handler.
Breaking change: HttpClient response streaming I .NET 10 är response streaming på som default i HttpClient. För de flesta JSON-API:er märks ingenting, men om du har gammal kod som läser response.Content efter att HttpClient har gjort sig av med svaret, kan du behöva opta ut: <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse> i .csproj.
Passkeys (WebAuthn) i Identity ASP.NET Core Identity har fått inbyggt stöd för passkeys — användare kan logga in med Touch ID, Windows Hello eller en säkerhetsnyckel i stället för lösenord. Blazor Web App-mallen innehåller scaffolding för passkeys när du väljer Individual Accounts. Bortom kursens scope men värt att känna till om du fortsätter med auth.

Referenser

Elektroniska resurser

Böcker

Övningar

Lös övningarna självständigt. Det finns inget facit — lärandet sker i processen.

  1. JSONPlaceholder-klient Bygg en typad PostsApi som anropar https://jsonplaceholder.typicode.com/posts. Visa alla inlägg i en lista och låt användaren klicka på ett för att se detaljer.
  2. CRUD mot lokalt API Skapa ett enkelt Minimal API som tjänar produkter (in-memory). Bygg en Blazor-sida som listar, skapar, uppdaterar och tar bort produkter via din typade klient.
  3. ErrorBoundary i praktiken Bygg en komponent som ibland kastar exception (slumpmässigt). Wrappa den i en ErrorBoundary med en återställningsknapp.
  4. Skapa ett auth-projekt Skapa ett nytt Blazor Web App med --auth Individual. Registrera ett konto, logga in/ut och bygg en sida som bara visas för inloggade. Lägg till en roll "Admin" manuellt i databasen och skydda en sida för Admin-roll.

Soloprojektor

Projekt 1 — Väderapp Anropa Open-Meteo (gratis, ingen nyckel: api.open-meteo.com) och visa väderprognos för en stad användaren väljer. Hantera laddning, fel och tom data snyggt.
Projekt 2 — Skyddad anteckningsbok (fördjupning) Bygg en anteckningsapp där varje inloggad användare har sina egna anteckningar. Använd ASP.NET Core Identity och ett eget Minimal API för CRUD. Skydda alla anteckningssidor.

← Föregående: State & DI Nästa: Render modes & deployment →