Condividi tramite


Chiamare una API web da ASP.NET Core Blazor

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo descrive come chiamare un'API Web da un'app Blazor .

Pacchetto

Il System.Net.Http.Json pacchetto fornisce metodi di estensione per System.Net.Http.HttpClient e System.Net.Http.HttpContent che eseguono la serializzazione automatica e la deserializzazione tramite System.Text.Json. Il System.Net.Http.Json pacchetto viene fornito dal framework condiviso .NET e non richiede l'aggiunta di un riferimento al pacchetto all'app.

Usare un gestore di token per le chiamate API Web

Blazor Web Apps con l'autenticazione OIDC può usare un approccio del gestore di token per effettuare richieste in uscita per proteggere le chiamate API Web esterne. Questo approccio viene usato dalle BlazorWebAppOidc app di esempio e BlazorWebAppOidcServer descritte nella sezione App di esempio di questo articolo.

Per ulteriori informazioni, vedi le seguenti risorse:

Microsoft Identity Platform per le chiamate API Web

Blazor Web App che usano la Microsoft Identity Platform con i pacchetti Web Identity di Microsoft per Entra ID possono effettuare chiamate API Web semplificate con l'API fornita dal pacchetto NuGet Microsoft.Identity.Web.DownstreamApi.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

Nel file delle impostazioni dell'app (appsettings.json) specificare un URL di base e ambiti. Nell'esempio seguente il {BASE ADDRESS} segnaposto è l'URL di base dell'API Web. Un singolo scopo viene specificato con un URI ID app ({APP ID URI} segnaposto) e il nome del scopo ({SCOPE NAME} segnaposto):

"DownstreamApi": {
  "BaseUrl": "{BASE ADDRESS}",
  "Scopes": [ "{APP ID URI}/{SCOPE NAME}" ]
}

Esempio:

"DownstreamApi": {
  "BaseUrl": "https://localhost:7277",
  "Scopes": [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ]
}

Nel file dell'app Program chiamare:

È possibile scegliere di crittografare la cache e farlo sempre nell'ambiente di produzione.

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDownstreamApi("DownstreamApi", 
        builder.Configuration.GetSection("DownstreamApi"))
    .AddDistributedTokenCaches();

// Requires the 'Microsoft.Extensions.Caching.Memory' NuGet package
builder.Services.AddDistributedMemoryCache();

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
    options => 
    {
        // The following lines that are commented out reflect
        // default values. We recommend overriding the default
        // value of Encrypt to encrypt tokens at rest.

        //options.DisableL1Cache = false;
        //options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024;
        options.Encrypt = true;
        //options.SlidingExpiration = TimeSpan.FromHours(1);
    });

Le cache dei token distribuiti in memoria vengono create quando si chiama AddDistributedTokenCaches per assicurarsi che sia disponibile un'implementazione di base per la memorizzazione nella cache dei token distribuiti.

Le app Web di produzione e le API Web devono usare una cache dei token distribuiti di produzione, ad esempio Redis, Microsoft SQL Server, Microsoft Azure Cosmos DB.

Nota

Per lo sviluppo locale e il test in un singolo computer, è possibile usare le cache dei token in memoria anziché le cache dei token distribuiti:

builder.Services.AddInMemoryTokenCaches();

Più avanti nel periodo di sviluppo e test, adottare un provider di cache distribuita di token in produzione.

AddDistributedMemoryCache aggiunge un'implementazione predefinita di che archivia gli elementi della IDistributedCache cache in memoria, che viene usata da Microsoft Identity Web per la memorizzazione nella cache dei token.

AddDistributedMemoryCache richiede un riferimento al Microsoft.Extensions.Caching.Memory pacchetto NuGet.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

Per configurare un provider di cache distribuita di produzione, vedere Memorizzazione nella cache distribuita in ASP.NET Core.

Avviso

Sostituire sempre le cache dei token distribuiti in memoria con un provider di cache dei token reale durante la distribuzione dell'app in un ambiente di produzione. Se non si adotta un provider di cache dei token distribuiti in produzione, l'app potrebbe subire un calo significativo delle prestazioni.

Per altre informazioni, vedere Serializzazione della cache dei token: Cache distribuita. Tuttavia, gli esempi di codice illustrati non si applicano alle app ASP.NET Core, che configurano le cache distribuite tramite AddDistributedMemoryCache, non AddDistributedTokenCache.

Usare un portachiavi di protezione dei dati condiviso nell'ambiente di produzione in modo che le istanze dell'app su server in una web farm possano decrittografare i token quando MsalDistributedTokenCacheAdapterOptions.Encrypt è impostato su true.

Nota

Per lo sviluppo anticipato e i test locali in un singolo computer, è possibile impostare Encryptfalse su e configurare un anello di chiave di protezione dati condiviso in un secondo momento:

options.Encrypt = false;

Più avanti nel periodo di sviluppo e test abilitare la crittografia dei token e adottare un anello di chiave di protezione dei dati condiviso.

L'esempio seguente illustra come usare Azure Blob Storage e Azure Key Vault (PersistKeysToAzureBlobStorage/ProtectKeysWithAzureKeyVault) per l'anello di chiavi condiviso. Le configurazioni del servizio sono scenari caso di base a scopo dimostrativo. Prima di distribuire le app di produzione, acquisire familiarità con i servizi di Azure e adottare le procedure consigliate usando i set di documentazione dedicati dei servizi di Azure, collegati alla fine di questa sezione.

Aggiungere i pacchetti seguenti al progetto server di Blazor Web App:

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

Nota

Prima di procedere con i passaggi seguenti, verificare che l'app sia registrata con Microsoft Entra.

Configurare Archiviazione BLOB di Azure per gestire le chiavi di protezione dei dati. Segui le indicazioni fornite in Provider di archiviazione chiavi in ASP.NET Core.

Configurare Azure Key Vault per crittografare le chiavi di protezione dei dati a riposo. Seguire le indicazioni in Configurare ASP.NET Protezione dati di base.

Usare il codice seguente nel file in Program cui vengono registrati i servizi:

TokenCredential? credential;

if (builder.Environment.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

builder.Services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential)
    .ProtectKeysWithAzureKeyVault(new Uri("{KEY IDENTIFIER}"), credential);

{MANAGED IDENTITY CLIENT ID}: ID client gestito di Azure Identity (GUID).

{TENANT ID}: ID del tenant.

{APPLICATION NAME}: SetApplicationName imposta il nome univoco dell'app all'interno del sistema di protezione dei dati. Il valore deve corrispondere tra le distribuzioni dell'app.

{BLOB URI}: URI completo del file di chiave. L'URI è generato da Azure Storage quando si crea il file della chiave. Non usare un SAS.

{KEY IDENTIFIER}: identificatore di chiave di Azure Key Vault usato per la crittografia delle chiavi. Un criterio di accesso consente all'applicazione di accedere al Key Vault con le autorizzazioni Get, Unwrap Key e Wrap Key. L'identificatore di chiave viene ottenuto dalla chiave nel portale Entra o Azure dopo la sua creazione. Se si abilita l'autorotazione della chiave del Key Vault, assicurarsi di utilizzare un identificatore di chiave senza versione nella configurazione del Key Vault dell'app, dove non viene inserito alcun GUID della chiave alla fine dell'identificatore (ad esempio: https://contoso.vault.azure.net/keys/data-protection).

Nota

Negli ambienti non di produzione, l'esempio precedente usa DefaultAzureCredential per semplificare l'autenticazione durante lo sviluppo di app distribuite in Azure combinando le credenziali usate negli ambienti di hosting di Azure con le credenziali usate nello sviluppo locale. Quando si passa alla produzione, un'alternativa è una scelta migliore, ad esempio quella ManagedIdentityCredential illustrata nell'esempio precedente. Per altre informazioni, vedere Autenticare le app .NET ospitate in Azure nelle risorse di Azure usando un'identità gestita assegnata dal sistema.

IDownstreamApi Inserire e chiamare CallApiForUserAsync quando si chiama per conto di un utente:

internal sealed class ServerWeatherForecaster(IDownstreamApi downstreamApi) : IWeatherForecaster
{
    public async Task<IEnumerable<WeatherForecast>> GetWeatherForecastAsync()
    {
        var response = await downstreamApi.CallApiForUserAsync("DownstreamApi",
            options =>
            {
                options.RelativePath = "/weather-forecast";
            });

        return await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
            throw new IOException("No weather forecast!");
    }
}

Questo approccio viene usato dalle BlazorWebAppEntra app di esempio e BlazorWebAppEntraBff descritte nella sezione App di esempio di questo articolo.

Per ulteriori informazioni, vedi le seguenti risorse:

App di esempio

Per esempi funzionanti, vedere le app di esempio seguenti nel Blazor repository GitHub degli esempi () (dotnet/blazor-samplescome scaricare).

BlazorWebAppCallWebApi

Chiamare un'API Web todo esterna (non nell'elenco di todo Blazor Web App) da un oggetto Blazor Web App.

  • Backend: un'app Web API per la gestione di un elenco di attività, basata su API minime. L'app per le API Web è un'app separata da Blazor Web App, possibilmente ospitata in un server diverso.
  • BlazorApp / BlazorApp.Client Blazor Web App: che chiama l'app API web con un HttpClient per le operazioni di elenco todo, ad esempio la creazione, la lettura, l'aggiornamento e l'eliminazione di elementi (CRUD) dall'elenco todo.

Per il rendering lato client (CSR), che include componenti Interactive WebAssembly e componenti Auto che hanno adottato CSR, le chiamate vengono effettuate con un HttpClient preconfigurato registrato nel file Program del progetto client (BlazorApp.Client):

builder.Services.AddScoped(sp =>
    new HttpClient
    {
        BaseAddress = new Uri(builder.Configuration["FrontendUrl"] ?? 
            "https://localhost:5002")
    });

Per il rendering lato server (SSR), che include componenti server prerenderizzati e interattivi, componenti WebAssembly prerenderizzati e componenti Auto prerenderizzati o che hanno adottato il SSR, le chiamate vengono effettuate con un HttpClient registrato nel file Program del progetto server (BlazorApp):

builder.Services.AddHttpClient();

Chiamare un'API interna di elenco di film (all'interno di Blazor Web App), in cui l'API risiede nel progetto server di Blazor Web App:

  • BlazorApp: oggetto Blazor Web App che gestisce un elenco di film:
    • Quando le operazioni vengono eseguite nell'elenco di film all'interno dell'app nel server, vengono usate le normali chiamate API.
    • Quando le chiamate API vengono effettuate da un client basato sul Web, viene usata un'API Web per le operazioni di elenco di film, in base alle API minime.
  • BlazorApp.Client: Il progetto del cliente di Blazor Web App, che include moduli WebAssembly interattivi e componenti automatici per la gestione degli utenti della lista di film.

Per CSR, che include componenti Interactive WebAssembly e Componenti automatici che hanno adottato csr, le chiamate all'API vengono effettuate tramite un servizio basato su client (ClientMovieService) che usa un file preconfigurato HttpClient registrato nel Program file del progetto client (BlazorApp.Client). Poiché queste chiamate vengono effettuate tramite un Web pubblico o privato, l'API elenco di film è un'API Web.

L'esempio seguente ottiene un elenco di film dall'endpoint /movies :

public class ClientMovieService(HttpClient http) : IMovieService
{
    public async Task<Movie[]> GetMoviesAsync(bool watchedMovies) => 
        await http.GetFromJsonAsync<Movie[]>("movies") ?? [];
}

Per SSR, che include componenti server prerenderati e interattivi, componenti WebAssembly prerenderati e componenti automatici prerenderati o adottati in SSR, le chiamate vengono effettuate direttamente tramite un servizio basato su server (ServerMovieService). L'API non si basa su una rete, quindi è un'API standard per le operazioni CRUD dell'elenco di film.

L'esempio seguente ottiene un elenco di film:

public class ServerMovieService(MovieContext db) : IMovieService
{
    public async Task<Movie[]> GetMoviesAsync(bool watchedMovies) => 
        watchedMovies ? 
        await db.Movies.Where(t => t.IsWatched).ToArrayAsync() : 
        await db.Movies.ToArrayAsync();
}

Per altre informazioni su come proteggere i dati dei film in questo scenario, vedere l'esempio di dati meteo descritto da Proteggere i dati in Blazor Web Apps con rendering automatico interattivo.

BlazorWebAppCallWebApi_Weather

Un'app di esempio di dati meteo che usa il rendering in streaming per i dati meteo.

BlazorWebAssemblyCallWebApi

Chiama un'API del Web per l'elenco delle cose da fare da un'app Blazor WebAssembly.

  • Backend: un'app Web API per la gestione di un elenco di attività, basata su API minime.
  • BlazorTodo Blazor WebAssembly: un'app che chiama l'API Web con un HttpClient preconfigurato per le operazioni CRUD dell'elenco delle cose da fare.

BlazorWebAssemblyStandaloneWithIdentity

Un'app Blazor WebAssembly autonoma protetta con ASP.NET Core Identity:

  • Backend: un'app per le API Web back-end che gestisce un archivio delle identità utente per ASP.NET Core Identity.
  • BlazorWasmAuth: un'app front-end autonoma Blazor WebAssembly con autenticazione utente.

La soluzione illustra la chiamata di un'API Web sicura per quanto segue:

  • Recupero dei ruoli di un utente autenticato.
  • Elaborazione dei dati per tutti gli utenti autenticati.
  • Elaborazione dei dati per gli utenti autorizzati (l'utente deve trovarsi nel ruolo Manager) tramite un criterio di autorizzazione .

BlazorWebAppOidc

Un Blazor Web App con interattività automatica globale che usa l'autenticazione OIDC con Microsoft Entra senza usare pacchetti specifici di Entra. L'esempio illustra come usare un gestore di token per le chiamate API Web per chiamare un'API Web protetta esterna.

BlazorWebAppOidcServer

Un server Blazor Web App con interattività globale che utilizza l'autenticazione OIDC con Microsoft Entra senza utilizzare pacchetti specifici di Entra. L'esempio illustra come passare un token di accesso per chiamare un'API Web protetta esterna.

BlazorWebAppOidcBff

Un Blazor Web App con interattività automatica globale che usa:

  • Autenticazione OIDC con Microsoft Entra senza usare pacchetti specifici di Entra.
  • Il pattern Backend per Frontend (BFF) di , un modello di sviluppo di app che crea servizi backend per le app frontend o le interfacce.

La soluzione include una dimostrazione di ottenere i dati meteo in modo sicuro tramite un'API Web esterna quando viene eseguito il rendering di un componente che adotta il rendering automatico interattivo nel client.

BlazorWebAppEntra

Un Blazor Web App con interattività automatica globale che usa Microsoft Identity Platform con Microsoft Identity Web packages per Microsoft Entra ID. La soluzione include una dimostrazione di ottenere i dati meteo in modo sicuro tramite un'API Web esterna quando viene eseguito il rendering di un componente che adotta il rendering automatico interattivo nel client.

BlazorWebAppEntraBff

Un Blazor Web App con interattività automatica globale che usa:

La soluzione include una dimostrazione di ottenere i dati meteo in modo sicuro tramite un'API Web esterna quando viene eseguito il rendering di un componente che adotta il rendering automatico interattivo nel client.

Eliminazione di HttpRequestMessage, HttpResponseMessagee HttpClient

Un HttpRequestMessage senza un corpo non richiede l'eliminazione esplicita con una using dichiarazione (C# 8 o versione successiva) o un using blocco (tutte le versioni C#), ma è consigliabile eliminare con ogni uso per i motivi seguenti:

  • Per ottenere un miglioramento delle prestazioni evitando i finalizzatori.
  • Per garantire che il codice sia solido in futuro, nel caso venga mai aggiunto un corpo della richiesta a un HttpRequestMessage che inizialmente non ne aveva uno.
  • Per evitare potenzialmente problemi funzionali se un gestore di delega prevede una chiamata a Dispose/DisposeAsync.
  • È più semplice applicare una regola generale ovunque che cercare di ricordare casi specifici.

Eliminare sempreHttpResponseMessage le istanze.

Non eliminare mai le istanze create chiamando perché sono gestite dal framework.

Esempio:

using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = clientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);

Scenari lato client per la chiamata di API Web esterne

I componenti basati su client chiamano API Web esterne usando istanze di HttpClient, in genere create con un HttpClient preconfigurato registrato nel file Program:

builder.Services.AddScoped(sp => 
    new HttpClient
    { 
        BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) 
    });

Il componente seguente Razor invia una richiesta a un'API Web per i rami GitHub in modo simile all'esempio utilizzo di base nell'articolo Effettuare richieste HTTP usando IHttpClientFactory in ASP.NET Core .

CallWebAPI.razor:

@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization
@inject HttpClient Client

<h1>Call web API from a Blazor WebAssembly Razor component</h1>

@if (getBranchesError || branches is null)
{
    <p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
    <ul>
        @foreach (var branch in branches)
        {
            <li>@branch.Name</li>
        }
    </ul>
}

@code {
    private IEnumerable<GitHubBranch>? branches = [];
    private bool getBranchesError;
    private bool shouldRender;

    protected override bool ShouldRender() => shouldRender;

    protected override async Task OnInitializedAsync()
    {
        using var request = new HttpRequestMessage(HttpMethod.Get,
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        using var response = await Client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            branches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(responseStream);
        }
        else
        {
            getBranchesError = true;
        }

        shouldRender = true;
    }

    public class GitHubBranch
    {
        [JsonPropertyName("name")]
        public string? Name { get; set; }
    }
}

Nell'esempio precedente per C# 12 o versione successiva viene creata una matrice vuota ([]) per la branches variabile. Per le versioni precedenti di C# compilate con un SDK precedente a .NET 8, creare una matrice vuota (Array.Empty<GitHubBranch>()).

Per proteggere il codice e i dati .NET/C#, usare ASP.NET funzionalità di protezione dei dati di base con un'API Web back-end ASP.NET Core sul lato server. L'app del lato client Blazor WebAssembly chiama l'API web lato server per le funzionalità sicure e l'elaborazione dei dati.

Blazor WebAssembly alle app viene spesso impedito di effettuare chiamate dirette alle API web a causa della sicurezza CORS (Cross-Origin Resource Sharing). Un'eccezione tipica è simile alla seguente:

Accesso al recupero da '{URL}' dall'origine 'https://localhost:{PORT}'' è stato bloccato dai criteri CORS: nessuna intestazione 'Access-Control-Allow-Origin' è presente nella risorsa richiesta. Se una risposta opaca soddisfa le proprie esigenze, impostare la modalità della richiesta su "no-cors" per recuperare la risorsa con CORS disabilitata.

Anche se si chiama SetBrowserRequestMode con un campo BrowserRequestMode di NoCors (1) che cerca di aggirare l'eccezione precedente, la richiesta spesso non riesce a causa di restrizioni CORS sull'origine dell'API Web, ad esempio una restrizione che consente solo chiamate da origini specifiche o una restrizione che impedisce richieste di fetch JavaScript da un browser. L'unico modo affinché tali chiamate abbiano successo è che l'API Web che stai chiamando consenta alla tua origine di effettuare chiamate alla sua origine con la configurazione CORS corretta. La maggior parte delle API Web esterne non consente di configurare i criteri CORS. Per gestire questa restrizione, adottare una delle strategie seguenti:

  • Gestisci il tuo server-side API web di backend ASP.NET Core. L'app Blazor WebAssembly sul lato client chiama l'API Web sul lato server, e quest'ultima effettua la richiesta, tramite il codice C# basato sul server (e non su un browser), verso l'API Web esterna con le corrette intestazioni CORS, restituendo poi il risultato all'app Blazor WebAssembly sul lato client.

  • Usare un servizio proxy per inoltrare la richiesta dall'app lato client Blazor WebAssembly all'API Web esterna. Il servizio proxy usa un'app lato server per effettuare la richiesta per conto del client e restituisce il risultato dopo che la chiamata ha esito positivo. Nell'esempio seguente basato sul proxy CORS di CloudFlare, il {REQUEST URI} segnaposto è l'URI della richiesta:

    @using System.Net
    @inject IHttpClientFactory ClientFactory
    
    ...
    
    @code {
        public async Task CallApi()
        {
            var client = ClientFactory.CreateClient();
    
            var urlEncodedRequestUri = WebUtility.UrlEncode("{REQUEST URI}");
    
            using var request = new HttpRequestMessage(HttpMethod.Get, 
                $"https://corsproxy.io/?{urlEncodedRequestUri}");
    
            using var response = await client.SendAsync(request);
    
            ...
        }
    }
    

Scenari lato server per la chiamata di API Web esterne

I componenti basati su server chiamano le API web esterne utilizzando istanze HttpClient, in genere create con IHttpClientFactory. Per indicazioni applicabili alle app lato server, vedere Effettuare richieste HTTP con IHttpClientFactory in ASP.NET Core.

Un'app server-side non include un servizio HttpClient. Fornire un oggetto HttpClient all'app utilizzando l'infrastruttura di factory HttpClient.

Nel file Program:

builder.Services.AddHttpClient();

Il componente seguente Razor invia una richiesta a un'API Web per i rami GitHub in modo simile all'esempio utilizzo di base nell'articolo Effettuare richieste HTTP usando IHttpClientFactory in ASP.NET Core .

CallWebAPI.razor:

@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization
@inject IHttpClientFactory ClientFactory

<h1>Call web API from a server-side Razor component</h1>

@if (getBranchesError || branches is null)
{
    <p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
    <ul>
        @foreach (var branch in branches)
        {
            <li>@branch.Name</li>
        }
    </ul>
}

@code {
    private IEnumerable<GitHubBranch>? branches = [];
    private bool getBranchesError;
    private bool shouldRender;

    protected override bool ShouldRender() => shouldRender;

    protected override async Task OnInitializedAsync()
    {
        using var request = new HttpRequestMessage(HttpMethod.Get,
            "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
        request.Headers.Add("Accept", "application/vnd.github.v3+json");
        request.Headers.Add("User-Agent", "HttpClientFactory-Sample");

        var client = ClientFactory.CreateClient();

        using var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            using var responseStream = await response.Content.ReadAsStreamAsync();
            branches = await JsonSerializer.DeserializeAsync
                <IEnumerable<GitHubBranch>>(responseStream);
        }
        else
        {
            getBranchesError = true;
        }

        shouldRender = true;
    }

    public class GitHubBranch
    {
        [JsonPropertyName("name")]
        public string? Name { get; set; }
    }
}

Nell'esempio precedente per C# 12 o versione successiva viene creata una matrice vuota ([]) per la branches variabile. Per le versioni precedenti di C# compilate con un SDK precedente a .NET 8, creare una matrice vuota (Array.Empty<GitHubBranch>()).

Per un esempio di lavoro supplementare, vedere l'esempio di caricamento di file lato server che carica i file in un controller API Web nell'articolo Blazor.

Astrazioni di servizio per le chiamate API web

Questa sezione si applica a Blazor Web Apps che gestisce un'API Web nel progetto server o trasforma le chiamate API Web a un'API Web esterna.

Quando si usano le modalità interattive WebAssembly e Rendering automatico, i componenti vengono pre-gestiti per impostazione predefinita.When using the interactive WebAssembly and Auto render mode, components are prerendered by default. Il rendering dei componenti automobilistici viene eseguito inizialmente anche in modo interattivo dal server, prima che il Blazor bundle venga scaricato nel client e che il runtime lato client venga attivato. Ciò significa che i componenti che usano queste modalità di rendering devono essere progettati in modo che vengano eseguiti correttamente sia dal client che dal server. Se il componente deve chiamare un'API basata su progetto server o trasformare una richiesta a un'API Web esterna (una esterna a Blazor Web App) quando è in esecuzione nel client, l'approccio consigliato consiste nell'astrarre tale chiamata API dietro un'interfaccia del servizio e implementare versioni client e server del servizio:

  • La versione client chiama l'API Web con un oggetto preconfigurato HttpClient.
  • La versione del server può in genere accedere direttamente alle risorse lato server. L'inserimento di un oggetto HttpClient nel server che effettua chiamate al server non è consigliato, perché in genere la richiesta di rete non è necessaria. In alternativa, l'API potrebbe essere esterna al progetto server, ma è necessaria un'astrazione del servizio per il server per trasformare la richiesta in qualche modo, ad esempio per aggiungere un token di accesso a una richiesta proxy.

Quando si usa la modalità di rendering WebAssembly, è anche possibile disabilitare la prerendering, in modo che i componenti eseguano solo il rendering dal client. Per ulteriori informazioni, vedere le modalità di rendering di ASP.NET Core.

Esempi (app di esempio):

  • API web per l'elenco dei film nell'app di esempio BlazorWebAppCallWebApi.
  • API Web per il rendering dei dati meteo in streaming nell'app BlazorWebAppCallWebApi_Weather di esempio.
  • Dati meteo restituiti al client nelle app di esempio BlazorWebAppOidc (modello non-BFF) o BlazorWebAppOidcBff (modello BFF). Queste app illustrano chiamate API sicure (Web). Per altre informazioni, vedere Proteggere un ASP.NET Core Blazor Web App con OpenID Connect (OIDC).

Blazor Web App API esterne del Web

Questa sezione si applica a Blazor Web Apps che chiamano un'API Web gestita da un progetto separato (esterno), possibilmente ospitato in un server diverso.

Blazor Web AppI componenti WebAssembly vengono generalmente prerenderizzati sul lato client e i componenti Auto vengono renderizzati sul server durante il rendering statico o interattivo lato server (SSR). HttpClient i servizi non vengono registrati di default nel progetto principale di Blazor Web App. Se l'app viene eseguita con solo i HttpClient servizi registrati nel .Client progetto, come descritto nella sezione Aggiungere il HttpClient servizio , l'esecuzione dell'app genera un errore di runtime:

InvalidOperationException: impossibile fornire un valore per la proprietà 'Http' nel tipo '... {COMPONENT}'. Non esiste alcun servizio registrato di tipo 'System.Net.Http.HttpClient'.

Usare uno degli approcci seguenti:

  • Aggiungere i HttpClient servizi al progetto server per rendere il HttpClient disponibile durante SSR. Utilizzare la seguente registrazione del servizio nel file Program del progetto server:

    builder.Services.AddHttpClient();
    

    HttpClient i servizi vengono forniti dal framework condiviso, quindi non è necessario un riferimento al pacchetto nel file di progetto dell'app.

    Esempio: API Web elenco todo nell'app BlazorWebAppCallWebApidi esempio

  • Se il prerendering non è necessario per un componente WebAssembly che chiama l'API Web, disabilitare il prerendering seguendo le indicazioni riportate in ASP.NET Core modalità di renderingBlazor. Se si adotta questo approccio, non è necessario aggiungere HttpClient servizi al progetto principale di Blazor Web App perché il componente non è prerenderato nel server.

Per ulteriori informazioni, vedere I servizi lato client falliscono nel risolversi durante la prerenderizzazione.

Dati prerisorsi

Quando si esegue la prerenderizzazione, i componenti effettuano il rendering due volte: prima in modo statico, quindi in modo interattivo. Lo stato non passa automaticamente dal componente prerenderato a quello interattivo. Se un componente esegue operazioni di inizializzazione asincrone ed esegue il rendering di contenuto diverso per stati diversi durante l'inizializzazione, ad esempio un "Caricamento..." indicatore di stato, è possibile che venga visualizzato uno sfarfallio quando il componente esegue il rendering due volte.

Per risolvere questo problema, è possibile scorrere lo stato prerenderato usando l'API Stato componente persistente, illustrata dalle BlazorWebAppCallWebApiBlazorWebAppCallWebApi_Weather e . Quando il componente esegue il rendering interattivo, può eseguire lo stesso rendering usando lo stesso stato. Tuttavia, l'API non funziona attualmente con lo spostamento avanzato, che è possibile aggirare disabilitando la navigazione avanzata nei collegamenti alla pagina (data-enhanced-nav=false). Per ulteriori informazioni, vedi le seguenti risorse:

Streaming delle richieste lato client

Per i browser basati su Chromium (ad esempio, Google Chrome e Microsoft Edge) usando il protocollo HTTP/2 e HTTPS, sul lato Blazor client viene usata l'API Streams per consentire lo streaming delle richieste.

Per abilitare lo streaming delle richieste, impostare SetBrowserRequestStreamingEnabled su true nel HttpRequestMessage.

Nell'esempio di caricamento del file seguente:

using var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
request.SetBrowserRequestStreamingEnabled(true);
request.Content = content;

using var response = await Http.SendAsync(request);

Richieste di streaming:

  • Richiedono il protocollo HTTPS e non funzionano su HTTP/1.x.
  • Includere un corpo del testo ma non un'intestazione Content-Length. CORS con una richiesta preliminare è necessaria per le richieste di streaming tra le origini.

Per ulteriori informazioni sui caricamenti di file con un componente InputFile, vedere Blazor e l'esempio in Caricare file su un server con rendering lato client (CSR).

Aggiungere il HttpClient servizio

Le indicazioni contenute in questa sezione si applicano agli scenari lato client.

I componenti lato client chiamano API Web usando un servizio preconfigurato, incentrato HttpClient sull'esecuzione di richieste al server di origine. È possibile creare configurazioni di servizio aggiuntive HttpClient per altre API Web nel codice per sviluppatori. Le richieste sono composte usando le funzioni di supporto JSON Blazor o con HttpRequestMessage. Le richieste possono includere la configurazione dell'opzione fetch API .

Gli esempi di configurazione in questa sezione sono utili solo quando viene chiamata una singola API Web per una singola HttpClient istanza nell'app. Quando l'app deve chiamare più API Web, ognuna con il proprio indirizzo di base e configurazione, è possibile adottare gli approcci seguenti, illustrati più avanti in questo articolo:

Aggiungere nel file Program un servizio HttpClient se non è già presente in un modello di progetto Blazor usato per creare l'app:

builder.Services.AddScoped(sp => 
    new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

L'esempio precedente imposta l'indirizzo di base con builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), che ottiene l'indirizzo di base per l'app ed è in genere derivato dal <base> valore del href tag nella pagina host.

I casi d'uso più comuni per l'uso dell'indirizzo di base del client sono:

  • Il progetto client (.Client) di ( Blazor Web App .NET 8 o versione successiva) effettua chiamate API Web dai componenti o dal codice WebAssembly eseguito nel client in WebAssembly alle API nell'app server.
  • Il progetto client (Client) di un'app ospitata Blazor WebAssembly effettua chiamate API Web al progetto server (Server). Si noti che il modello di progetto Hosted Blazor WebAssembly non è più disponibile in .NET 8 o versione successiva. Tuttavia, le app ospitate Blazor WebAssembly rimangono supportate per .NET 8.

Se si chiama un'API Web esterna (non nello stesso spazio URL dell'app client), impostare l'URI sull'indirizzo di base dell'API Web. L'esempio seguente imposta l'indirizzo di base dell'API Web su https://localhost:5001, in cui un'app per le API Web separata è in esecuzione e pronta per rispondere alle richieste dall'app client:

builder.Services.AddScoped(sp => 
    new HttpClient { BaseAddress = new Uri("https://localhost:5001") });

Utilità JSON

HttpClient è disponibile come servizio preconfigurato per l'esecuzione di richieste al server di origine.

HttpClient e gli helper JSON (System.Net.Http.Json.HttpClientJsonExtensions) vengono usati anche per chiamare endpoint API Web di terze parti. HttpClientviene implementato usando l'API Fetch del browser ed è soggetto alle relative limitazioni, inclusa l'applicazione dei criteri di stessa origine, che viene descritta più avanti in questo articolo nella sezione Condivisione risorse tra le origini (CORS).

L'indirizzo di base del client è impostato sull'indirizzo del server di origine. Inserire un'istanza HttpClient in un componente usando la direttiva @inject.

@using System.Net.Http
@inject HttpClient Http

Usare lo spazio dei nomi System.Net.Http.Json per l'accesso a HttpClientJsonExtensions, tra cui GetFromJsonAsync, PutAsJsonAsync, e PostAsJsonAsync:

@using System.Net.Http.Json

Le sezioni seguenti illustrano gli helper JSON:

System.Net.Http include metodi aggiuntivi per l'invio di richieste HTTP e la ricezione di risposte HTTP, ad esempio per inviare una richiesta DELETE. Per altre informazioni, vedere la sezione DELETE e metodi di estensione aggiuntivi .

GET da JSON (GetFromJsonAsync)

GetFromJsonAsync invia una richiesta HTTP GET e analizza il corpo della risposta JSON per creare un oggetto.

Nel codice del componente seguente, i todoItems sono visualizzati dal componente. GetFromJsonAsync viene chiamato al termine dell'inizializzazione del componente (OnInitializedAsync).

todoItems = await Http.GetFromJsonAsync<TodoItem[]>("todoitems");

POST con JSON (PostAsJsonAsync)

PostAsJsonAsync invia una richiesta POST all'URI specificato contenente il valore serializzato come JSON nel corpo della richiesta.

Nel seguente codice del componente, newItemName è fornito da un elemento associato del componente. Il AddItem metodo viene attivato selezionando un <button> elemento .

await Http.PostAsJsonAsync("todoitems", addItem);

PostAsJsonAsync restituisce un oggetto HttpResponseMessage. Per deserializzare il contenuto JSON dal messaggio di risposta, usare il metodo di ReadFromJsonAsync estensione. L'esempio seguente legge i dati meteo JSON come matrice:

var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ?? 
    Array.Empty<WeatherForecast>();

PUT come JSON (PutAsJsonAsync)

PutAsJsonAsync invia una richiesta HTTP PUT con contenuto con codifica JSON.

Nel codice del componente seguente i editItem valori per Name e IsCompleted vengono forniti da elementi associati del componente. L'attributo Id dell'elemento è impostato quando l'elemento viene selezionato in un'altra parte dell'interfaccia utente (non visualizzata) e EditItem viene chiamato. Il SaveItem metodo viene attivato selezionando l'elemento <button> . L'esempio seguente non mostra il caricamento todoItems per brevità. Per un esempio di caricamento degli elementi, vedere la sezione GET from JSON (GetFromJsonAsync).

await Http.PutAsJsonAsync($"todoitems/{editItem.Id}", editItem);

PutAsJsonAsync restituisce un oggetto HttpResponseMessage. Per deserializzare il contenuto JSON dal messaggio di risposta, usare il metodo di ReadFromJsonAsync estensione. L'esempio seguente legge i dati meteo JSON come matrice:

var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ?? 
    Array.Empty<WeatherForecast>();

PATCH come JSON (PatchAsJsonAsync)

PatchAsJsonAsync invia una richiesta HTTP PATCH con contenuto con codifica JSON.

Nota

Per altre informazioni, vedere JsonPatch in ASP.NET API Web Core.

Nell'esempio PatchAsJsonAsync seguente riceve un documento JSON PATCH come stringa di testo normale con virgolette di escape:

await Http.PatchAsJsonAsync(
    $"todoitems/{id}", 
    "[{\"operationType\":2,\"path\":\"/IsComplete\",\"op\":\"replace\",\"value\":true}]");

A partire da C# 11 (.NET 7), è possibile comporre una stringa JSON come una stringa letterale raw . Specificare la sintassi JSON con il campo StringSyntaxAttribute.Json per l'attributo [StringSyntax] negli strumenti di analisi del codice.

@using System.Diagnostics.CodeAnalysis

...

@code {
    [StringSyntax(StringSyntaxAttribute.Json)]
    private const string patchOperation =
        """[{"operationType":2,"path":"/IsComplete","op":"replace","value":true}]""";

    ...

    await Http.PatchAsJsonAsync($"todoitems/{id}", patchOperation);
}

PatchAsJsonAsync restituisce un oggetto HttpResponseMessage. Per deserializzare il contenuto JSON dal messaggio di risposta, usare il metodo di ReadFromJsonAsync estensione. L'esempio seguente legge i dati dell'elemento todo JSON come matrice. Se non vengono restituiti dati di elemento dal metodo, viene creata una matrice vuota, quindi content non è null dopo l'esecuzione dell'istruzione:

using var response = await Http.PatchAsJsonAsync(...);
var content = await response.Content.ReadFromJsonAsync<TodoItem[]>() ??
    Array.Empty<TodoItem>();

Disposto con indentazione, spaziatura e virgolette non escape, il documento PATCH non codificato viene visualizzato come il seguente JSON.

[
  {
    "operationType": 2,
    "path": "/IsComplete",
    "op": "replace",
    "value": true
  }
]

Per semplificare la creazione di documenti PATCH nell'app che emette richieste PATCH, un'app può usare il supporto PATCH JSON .NET, come illustrato nelle indicazioni seguenti.

Installare il Microsoft.AspNetCore.JsonPatch.SystemTextJson pacchetto NuGet e usare le funzionalità API del pacchetto per comporre un oggetto JsonPatchDocument per una richiesta PATCH.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

Aggiungere direttive per i namespace @using, System.Text.Json, System.Text.Json.Serialization e Microsoft.AspNetCore.JsonPatch.SystemTextJson all'inizio del componente Razor.

@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch.SystemTextJson

Comporre il JsonPatchDocument per il TodoItem con IsComplete impostato su true utilizzando il metodo JsonPatchDocument.Replace.

var patchDocument = new JsonPatchDocument<TodoItem>()
    .Replace(p => p.IsComplete, true);

Installare il Microsoft.AspNetCore.JsonPatch pacchetto NuGet e usare le funzionalità API del pacchetto per comporre un oggetto JsonPatchDocument per una richiesta PATCH.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

Aggiungere @using direttive per i namespace System.Text.Json, System.Text.Json.Serialization e Microsoft.AspNetCore.JsonPatch all'inizio del componente Razor:

@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch

Comporre il JsonPatchDocument per il TodoItem con IsComplete impostato su true utilizzando il metodo Replace.

var patchDocument = new JsonPatchDocument<TodoItem>()
    .Replace(p => p.IsComplete, true);

Passare le operazioni del documento (patchDocument.Operations) alla chiamata PatchAsJsonAsync:

private async Task UpdateItem(long id)
{
    await Http.PatchAsJsonAsync(
        $"todoitems/{id}", 
        patchDocument.Operations, 
        new JsonSerializerOptions()
        {
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
        });
}

JsonSerializerOptions.DefaultIgnoreCondition è impostato su JsonIgnoreCondition.WhenWritingDefault per ignorare una proprietà solo se è uguale al valore predefinito per il relativo tipo.

Aggiungere JsonSerializerOptions.WriteIndented impostato su true se si vuole presentare il payload JSON in un formato piacevole per la visualizzazione. La scrittura di JSON con rientro non ha alcun impatto sull'elaborazione delle richieste PATCH e in genere non viene eseguita nelle app di produzione per le richieste api Web.

Per aggiungere un'azione del controller PATCH all'API Web, seguire le indicazioni riportate nell'articolo JsonPatch in ASP.NET API Web . In alternativa, l'elaborazione delle richieste PATCH può essere implementata come API minima con la procedura seguente.

Aggiungere un riferimento al pacchetto NuGet all'applicazione web API Microsoft.AspNetCore.JsonPatch.SystemTextJson.

Nel file Program, aggiungere una direttiva @using per lo spazio dei nomi Microsoft.AspNetCore.JsonPatch.SystemTextJson.

using Microsoft.AspNetCore.JsonPatch.SystemTextJson;

Aggiungere un riferimento al pacchetto NuGet all'applicazione web API Microsoft.AspNetCore.Mvc.NewtonsoftJson.

Nota

Non è necessario aggiungere un riferimento al pacchetto Microsoft.AspNetCore.JsonPatch nell'app perché il riferimento al pacchetto Microsoft.AspNetCore.Mvc.NewtonsoftJson aggiunge automaticamente in modo transitivo un riferimento al pacchetto Microsoft.AspNetCore.JsonPatch.

Nel file Program aggiungere una direttiva @using per lo spazio dei nomi Microsoft.AspNetCore.JsonPatch:

using Microsoft.AspNetCore.JsonPatch;

Fornire l'endpoint alla pipeline di elaborazione delle richieste dell'API Web:

app.MapPatch("/todoitems/{id}", async (long id, TodoContext db) =>
{
    if (await db.TodoItems.FindAsync(id) is TodoItem todo)
    {
        var patchDocument = 
            new JsonPatchDocument<TodoItem>().Replace(p => p.IsComplete, true);
        patchDocument.ApplyTo(todo);
        await db.SaveChangesAsync();

        return TypedResults.Ok(todo);
    }

    return TypedResults.NoContent();
});

Avviso

Come per gli altri esempi nell'articolo JsonPatch in ASP.NET Core Web API, la precedente PATCH API non protegge il Web API da attacchi di sovrappostaggio. Per altre informazioni, vedere Esercitazione: Creare un'API Web basata su controller con ASP.NET Core.

Per un'esperienza PATCH completamente funzionante, vedi l'app BlazorWebAppCallWebApidi esempio.

DELETE (DeleteAsync) e metodi di estensione aggiuntivi

System.Net.Http include metodi di estensione aggiuntivi per l'invio di richieste HTTP e la ricezione di risposte HTTP. HttpClient.DeleteAsync viene usato per inviare una richiesta HTTP DELETE a un'API Web.

Nel codice del componente seguente l'elemento <button> chiama il DeleteItem metodo . L'elemento vincolato <input> fornisce la componente id dell'elemento da eliminare.

await Http.DeleteAsync($"todoitems/{id}");

Chiamato HttpClient insieme a IHttpClientFactory

I servizi e la configurazione di un elemento nominato IHttpClientFactory sono supportati da HttpClient.

Nota

Un'alternativa all'uso di un oggetto denominato HttpClient da un IHttpClientFactory è utilizzare un oggetto tipizzato HttpClient. Per altre informazioni, vedere la sezione Tipizzata HttpClient .

Aggiungere il Microsoft.Extensions.Http pacchetto NuGet all'app.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

Program Nel file di un progetto del cliente:

builder.Services.AddHttpClient("WebAPI", client => 
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));

Se il client denominato deve essere usato dai componenti lato client prerenderizzati di un Blazor Web App, la registrazione del servizio precedente deve apparire sia nel progetto server sia nel progetto .Client. Nel server viene builder.HostEnvironment.BaseAddress sostituito dall'indirizzo di base dell'API Web, descritto più avanti.

L'esempio precedente lato client imposta l'indirizzo di base utilizzando builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), che ottiene l'indirizzo di base per l'applicazione client ed è in genere derivato dal valore del tag <base> nella pagina host.

I casi d'uso più comuni per l'uso dell'indirizzo di base del client sono:

  • Il progetto client (.Client) di un progetto di tipo Blazor Web App che effettua chiamate API Web dai componenti in WebAssembly/Auto o dal codice che gira nel client in WebAssembly verso le API nell'app del server allo stesso indirizzo host.
  • Progetto client (Client) di un'app ospitata Blazor WebAssembly che effettua chiamate API Web al progetto server (Server).

Il caso d'uso più comune per l'uso del proprio indirizzo di base del client è nel progetto client (Client) di un'app ospitata Blazor WebAssembly che effettua chiamate API Web al progetto server (Server).

Se si chiama un'API Web esterna (non nello stesso spazio URL dell'app client) o si configurano i servizi in un'app lato server (ad esempio per gestire la pre-gestione dei componenti lato client nel server), impostare l'URI sull'indirizzo di base dell'API Web. L'esempio seguente imposta l'indirizzo di base dell'API Web su https://localhost:5001, in cui un'app per le API Web separata è in esecuzione e pronta per rispondere alle richieste dall'app client:

builder.Services.AddHttpClient("WebAPI", client => 
    client.BaseAddress = new Uri("https://localhost:5001"));

Nel codice del componente seguente:

  • Un'istanza di IHttpClientFactory crea un oggetto denominato HttpClient.
  • Il denominato HttpClient viene usato per emettere una richiesta GET per i dati delle previsioni meteo JSON dall'API Web all'indirizzo /forecast.
@inject IHttpClientFactory ClientFactory

...

@code {
    private Forecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        var client = ClientFactory.CreateClient("WebAPI");

        forecasts = await client.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
    }
}

L'app BlazorWebAppCallWebApi di esempio illustra la chiamata di un'API Web con un elemento di nome HttpClient nel suo componente CallTodoWebApiCsrNamedClient. Per una dimostrazione di lavoro aggiuntiva in un'app client basata sulla chiamata a Microsoft Graph con un denominato HttpClient, vedere Usare l'API Graph con ASP.NET Core Blazor WebAssembly.

Per una dimostrazione funzionante in un'app client basata sulla chiamata a Microsoft Graph con un denominato HttpClient, vedere Usare l'API Graph con ASP.NET Core Blazor WebAssembly.

Digitato HttpClient

Il tipo HttpClient utilizza una o più istanze dell'app HttpClient, predefinita o denominata, per restituire dati da uno o più endpoint API web.

Nota

Un'alternativa all'uso di un HttpClient tipizzato è utilizzare un HttpClient denominato da un IHttpClientFactory. Per altre informazioni, vedere la sezione Named HttpClient with IHttpClientFactory .

Aggiungere il Microsoft.Extensions.Http pacchetto NuGet all'app.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli in Installare e gestire pacchetti in Flusso di lavoro a consumo di pacchetti (documentazione di NuGet). Confermare le versioni corrette dei pacchetti in NuGet.org.

L'esempio seguente invia una richiesta GET per i dati delle previsioni meteo JSON dall'API Web all'indirizzo /forecast.

ForecastHttpClient.cs:

using System.Net.Http.Json;

namespace BlazorSample.Client;

public class ForecastHttpClient(HttpClient http)
{
    public async Task<Forecast[]> GetForecastAsync() => 
        await http.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
}

Program Nel file di un progetto del cliente:

builder.Services.AddHttpClient<ForecastHttpClient>(client => 
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));

Se il client tipizzato deve essere usato dai componenti lato client prerenderizzati di un Blazor Web App, la registrazione del servizio precedente deve essere inclusa sia nel progetto server che nel progetto .Client. Nel server viene builder.HostEnvironment.BaseAddress sostituito dall'indirizzo di base dell'API Web, descritto più avanti.

L'esempio precedente imposta l'indirizzo di base con builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), che ottiene l'indirizzo di base per l'app lato client ed è in genere derivato dal <base> valore del href tag nella pagina host.

I casi d'uso più comuni per l'uso dell'indirizzo di base del client sono:

  • Il progetto client (.Client) di un progetto di tipo Blazor Web App che effettua chiamate API Web dai componenti in WebAssembly/Auto o dal codice che gira nel client in WebAssembly verso le API nell'app del server allo stesso indirizzo host.
  • Progetto client (Client) di un'app ospitata Blazor WebAssembly che effettua chiamate API Web al progetto server (Server).

Il caso d'uso più comune per l'uso del proprio indirizzo di base del client è nel progetto client (Client) di un'app ospitata Blazor WebAssembly che effettua chiamate API Web al progetto server (Server).

Se si chiama un'API Web esterna (non nello stesso spazio URL dell'app client) o si configurano i servizi in un'app lato server (ad esempio per gestire la pre-gestione dei componenti lato client nel server), impostare l'URI sull'indirizzo di base dell'API Web. L'esempio seguente imposta l'indirizzo di base dell'API Web su https://localhost:5001, in cui un'app per le API Web separata è in esecuzione e pronta per rispondere alle richieste dall'app client:

builder.Services.AddHttpClient<ForecastHttpClient>(client => 
    client.BaseAddress = new Uri("https://localhost:5001"));

I componenti inseriscono l'oggetto digitato HttpClient per chiamare l'API web.

Nel codice del componente seguente:

  • Viene iniettata un'istanza del precedente ForecastHttpClient, che crea un oggetto tipizzato HttpClient.
  • Il testo digitato HttpClient viene utilizzato per emettere una richiesta GET per i dati delle previsioni meteorologiche JSON dall'API web.
@inject ForecastHttpClient Http

...

@code {
    private Forecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await Http.GetForecastAsync();
    }
}

L'app BlazorWebAppCallWebApi di esempio dimostra come chiamare un'API Web tipizzata nel suo componente relativo HttpClient. Si noti che il componente adotta il client-side rendering (CSR) (InteractiveWebAssembly modalità di rendering) con prerendering, quindi la registrazione del servizio client tipizzata appare nel file Program sia del progetto server che del progetto .Client.

Le indicazioni contenute in questa sezione si applicano agli scenari lato client che si basano su un'autenticazione cookie.

Per l'autenticazione basata su cookie, considerata più sicura dell'autenticazione basata sul token portatore, le credenziali cookie possono essere inviate con ogni richiesta API web chiamando AddHttpMessageHandler con un DelegatingHandler su un HttpClient preconfigurato. Il gestore configura SetBrowserRequestCredentials con BrowserRequestCredentials.Include, che consiglia al browser di inviare le credenziali con ogni richiesta, ad esempio cookie o intestazioni di autenticazione HTTP, incluse le richieste tra le origini.

CookieHandler.cs:

public class CookieHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
        request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);

        return base.SendAsync(request, cancellationToken);
    }
}

Nota

Per indicazioni su come accedere a AuthenticationStateProvider da un DelegatingHandler oggetto, vedere ASP.NET Core server-side e Blazor Web App altri scenari di sicurezza.

L'oggetto CookieHandler è registrato nel Program file :

builder.Services.AddTransient<CookieHandler>();

Il gestore messaggi viene aggiunto a qualsiasi HttpClient preconfigurato che richiede l'autenticazione cookie.

builder.Services.AddHttpClient(...)
    .AddHttpMessageHandler<CookieHandler>();

Per una dimostrazione, vedere Secure ASP.NET Core Blazor WebAssembly con ASP.NET Core Identity.

Quando componi un HttpRequestMessage, imposta direttamente le credenziali e l'intestazione della richiesta del browser.

using var request = new HttpRequestMessage() { ... };

request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);

HttpClient e HttpRequestMessage con le opzioni di richiesta dell'API Fetch

Le linee guida contenute in questa sezione si applicano agli scenari lato client che si basano sull'autenticazione tramite token portatore.

HttpClient (documentazione dell'API) e HttpRequestMessage può essere usato per personalizzare le richieste. Ad esempio, è possibile specificare il metodo HTTP e le intestazioni della richiesta. Il componente seguente effettua una POST richiesta a un endpoint API Web e mostra il corpo della risposta.

TodoRequest.razor:

@page "/todo-request"
@using System.Net.Http.Headers
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider

<h1>ToDo Request</h1>

<h1>ToDo Request Example</h1>

<button @onclick="PostRequest">Submit POST request</button>

<p>Response body returned by the server:</p>

<p>@responseBody</p>

@code {
    private string? responseBody;

    private async Task PostRequest()
    {
        using var request = new HttpRequestMessage()
        {
            Method = new HttpMethod("POST"),
            RequestUri = new Uri("https://localhost:10000/todoitems"),
            Content =
                JsonContent.Create(new TodoItem
                {
                    Name = "My New Todo Item",
                    IsComplete = false
                })
        };

        var tokenResult = await TokenProvider.RequestAccessToken();

        if (tokenResult.TryGetToken(out var token))
        {
            request.Headers.Authorization =
                new AuthenticationHeaderValue("Bearer", token.Value);

            request.Content.Headers.TryAddWithoutValidation(
                "x-custom-header", "value");

            using var response = await Http.SendAsync(request);
            var responseStatusCode = response.StatusCode;

            responseBody = await response.Content.ReadAsStringAsync();
        }
    }

    public class TodoItem
    {
        public long Id { get; set; }
        public string? Name { get; set; }
        public bool IsComplete { get; set; }
    }
}

BlazorL'implementazione lato client di HttpClient usa l'API Fetch e configura le opzioni dell'API fetch specifiche della richiesta sottostanti tramite HttpRequestMessage metodi di estensione e WebAssemblyHttpRequestMessageExtensions. Impostare opzioni aggiuntive usando il metodo di estensione generico SetBrowserRequestOption . Blazor e l'API Fetch sottostante non aggiungono o modificano direttamente le intestazioni delle richieste. Per altre informazioni sul modo in cui gli agenti utente, ad esempio i browser, interagiscono con le intestazioni, consultare gli insiemi di documentazione esterni degli agenti utente e altre risorse web.

Lo streaming delle risposte è abilitato per impostazione predefinita.

La chiamata HttpContent.ReadAsStreamAsync per un HttpResponseMessage.Content (response.Content.ReadAsStreamAsync()) restituisce un BrowserHttpReadStream (sorgente di riferimento), non un oggetto MemoryStream. BrowserHttpReadStream non supporta operazioni sincrone, ad esempio Stream.Read(Span<Byte>). Se il codice utilizza operazioni sincrone, è possibile evitare lo streaming delle risposte o copiare il Stream in un MemoryStream.

Nota

I collegamenti della documentazione alla referenza di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Cambia rami o tag . Per ulteriori informazioni, vedere Come selezionare un tag di versione del codice sorgente di ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Per rifiutare esplicitamente lo streaming di risposte a livello globale, usare uno degli approcci seguenti:

  • Aggiungere la <WasmEnableStreamingResponse> proprietà al file di progetto con il valore false:

    <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>
    
  • Impostare la DOTNET_WASM_ENABLE_STREAMING_RESPONSE variabile di ambiente su false o 0.

Per disattivare il flusso di risposte per una singola richiesta, impostare SetBrowserResponseStreamingEnabled su false nel HttpRequestMessage (request nell'esempio seguente):

request.SetBrowserResponseStreamingEnabled(false);

La risposta HTTP viene in genere memorizzata nel buffer per abilitare il supporto per le letture sincrone sul contenuto della risposta. Per abilitare il supporto per il flusso di risposte, impostare SetBrowserResponseStreamingEnabled su true in HttpRequestMessage:

request.SetBrowserResponseStreamingEnabled(true);

Per impostazione predefinita, HttpCompletionOption.ResponseContentRead viene configurato, il che comporta che il HttpClient venga completato dopo la lettura dell'intera risposta, incluso il contenuto. Per poter usare l'opzione SetBrowserResponseStreamingEnabled su file di grandi dimensioni, impostare HttpCompletionOption.ResponseHeadersRead per evitare di memorizzare nella cache il contenuto del file in memoria:

- using var response = await Http.SendAsync(request);
+ using var response = await Http.SendAsync(request, 
+     HttpCompletionOption.ResponseHeadersRead);

Per includere le credenziali in una richiesta tra origini, usare il metodo di estensione SetBrowserRequestCredentials.

request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

Per altre informazioni sulle opzioni dell'API Fetch, vedere La documentazione Web MDN: WindowOrWorkerGlobalScope.fetch(): Parameters.

Gestione degli errori

Gestire gli errori di risposta dell'API Web nel codice dello sviluppatore quando si verificano. Ad esempio, GetFromJsonAsync prevede di ricevere una risposta JSON dall'API Web con un Content-Type di application/json. Se la risposta non è in formato JSON, la convalida del contenuto genera un'eccezione NotSupportedException.

Nell'esempio seguente l'endpoint URI per la richiesta di dati delle previsioni meteo viene digitato in modo non corretto. L'URI dovrebbe essere WeatherForecast ma appare nella chiamata come WeatherForcast, a cui manca la lettera e in Forecast.

La chiamata GetFromJsonAsync si aspetta che venga restituito JSON, ma l'API web restituisce HTML per un'eccezione non gestita con valore Content-Type di text/html. L'eccezione non gestita si verifica perché il percorso di /WeatherForcast non viene trovato e il middleware non può gestire una pagina o una visualizzazione per la richiesta.

In OnInitializedAsync, NotSupportedException viene generata sul client quando il contenuto della risposta viene validato come non JSON. L'eccezione viene intercettata nel blocco catch, dove la logica personalizzata potrebbe registrare l'errore o presentare un messaggio di errore amichevole all'utente.

ReturnHTMLOnException.razor:

@page "/return-html-on-exception"
@using {PROJECT NAME}.Shared
@inject HttpClient Http

<h1>Fetch data but receive HTML on unhandled exception</h1>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <h2>Temperatures by Date</h2>

    <ul>
        @foreach (var forecast in forecasts)
        {
            <li>
                @forecast.Date.ToShortDateString():
                @forecast.TemperatureC &#8451;
                @forecast.TemperatureF &#8457;
            </li>
        }
    </ul>
}

<p>
    @exceptionMessage
</p>

@code {
    private WeatherForecast[]? forecasts;
    private string? exceptionMessage;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            // The URI endpoint "WeatherForecast" is misspelled on purpose on the 
            // next line. See the preceding text for more information.
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForcast");
        }
        catch (NotSupportedException exception)
        {
            exceptionMessage = exception.Message;
        }
    }
}

Nota

L'esempio precedente è a scopo dimostrativo. È possibile configurare un'API Web per restituire JSON anche quando un endpoint non esiste o si verifica un'eccezione non gestita nel server.

Per altre informazioni, vedere Gestire gli errori nelle app ASP.NET CoreBlazor.

Condivisione delle Risorse tra Origini Diverse (CORS)

La sicurezza del browser spesso impedisce a una pagina Web di effettuare richieste a un'origine diversa da quella che ha servito la pagina Web. Questa restrizione è denominata criterio di origine identica. I criteri di stessa origine limitano (ma non impediscono) a un sito dannoso di leggere i dati sensibili da un altro sito. Per effettuare richieste dal browser a un endpoint con un'origine diversa, l'endpoint deve abilitare la condivisione di risorse tra le origini (CORS).

Per altre informazioni su CORS sul lato server, vedere Abilitare le richieste tra le origini (CORS) in ASP.NET Core. Gli esempi dell'articolo non riguardano direttamente Razor gli scenari dei componenti, ma l'articolo è utile per apprendere concetti CORS generali.

Per informazioni sulle richieste CORS sul lato client, vedere Blazor WebAssembly.

Supporto antiforgerato

Per aggiungere il supporto antiforgery a una richiesta HTTP, inserire il AntiforgeryStateProvider e aggiungere un RequestToken alla raccolta di intestazioni come RequestVerificationToken:

@inject AntiforgeryStateProvider Antiforgery
private async Task OnSubmit()
{
    var antiforgery = Antiforgery.GetAntiforgeryToken();
    using var request = new HttpRequestMessage(HttpMethod.Post, "action");
    request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
    using var response = await client.SendAsync(request);
    ...
}

Per altre informazioni, vedere autenticazione e autorizzazione di base ASP.NETBlazor.

Blazor Esempi di componenti framework per il test dell'accesso all'API Web

Vari strumenti di rete sono disponibili pubblicamente per testare direttamente le app back-end dell'API Web, ad esempio Firefox Browser Developer. Blazor L'origine di riferimento del framework include HttpClient asset di test utili per i test:

HttpClientTest asset nel dotnet/aspnetcore repository GitHub

Nota

I collegamenti della documentazione alla referenza di .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Cambia rami o tag . Per ulteriori informazioni, vedere Come selezionare un tag di versione del codice sorgente di ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Risorse aggiuntive

Generale

Mitigazione degli attacchi di esagerazione nei post

Le Web API possono essere vulnerabili a un attacco di sovrapostaggio, noto anche come attacco di assegnazione multipla. Un attacco di overposting si verifica quando un utente malintenzionato rilascia un POST in formato HTML al server che elabora i dati per le proprietà che non fanno parte del modulo sottoposto a rendering e che lo sviluppatore non vuole consentire agli utenti di modificare. Il termine "overposting" significa letteralmente che l'utente malintenzionato ha inviato troppi dati tramite il formulario.

Per indicazioni sulla mitigazione degli attacchi di overposting, vedere Esercitazione: Creare un'API Web basata su controller con ASP.NET Core.

Lato del server

Lato del client