Compartir a través de


Llamar a una API web desde ASP.NET Core Blazor

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la Política de Soporte de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión de .NET 9 de este artículo.

En este artículo se describe cómo llamar a una API web desde una aplicación Blazor.

Paquete

El paquete System.Net.Http.Json proporciona métodos de extensión para System.Net.Http.HttpClient y System.Net.Http.HttpContent que realizan automáticamente la serialización y deserialización mediante System.Text.Json. El marco compartido de .NET proporciona el paquete System.Net.Http.Json y no requiere que se agregue una referencia de paquete a la aplicación.

Uso de un controlador de tokens para llamadas API web

Blazor Web Apps con la autenticación de OIDC pueden usar un enfoque de controlador de tokens para realizar solicitudes salientes y asegurar las llamadas a APIs web externas. Las aplicaciones de ejemplo BlazorWebAppOidc y BlazorWebAppOidcServer que se describen en la sección Aplicaciones de ejemplo de este artículo usan este enfoque.

Para obtener más información, consulta los siguientes recursos:

Plataforma de identidad de Microsoft para llamadas API web

Blazor Web Appaquellos que utilizan la plataforma de identidad de Microsoft con paquetes web de Microsoft Identity para Microsoft Entra ID pueden hacer llamadas simplificadas a la API web con la API proporcionada por el Microsoft.Identity.Web.DownstreamApi paquete NuGet.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

En el archivo de configuración de la aplicación (appsettings.json), proporcione una dirección URL base y ámbitos. En el ejemplo siguiente, el {BASE ADDRESS} marcador de posición es la dirección URL base de la API web. Se especifica un único ámbito con un URI de ID de aplicación ({APP ID URI} elemento de marcador de posición) y un nombre de ámbito ({SCOPE NAME} elemento de marcador de posición):

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

Ejemplo:

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

En el archivo de la aplicación Program, llame a:

Puede optar por cifrar la memoria caché y siempre debe hacerlo en producción.

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);
    });

Las memorias caché de tokens distribuidos en memoria se crean al llamar a AddDistributedTokenCaches para garantizar que exista una implementación base disponible para el almacenamiento en caché de tokens distribuidos.

Las aplicaciones web de producción y las API web deben usar una caché de tokens distribuidos de producción (por ejemplo: Redis, Microsoft SQL Server, Microsoft Azure Cosmos DB).

Nota

Para el desarrollo y las pruebas locales en una sola máquina, puede usar cachés de tokens en memoria en lugar de cachés de tokens distribuidos:

builder.Services.AddInMemoryTokenCaches();

Más adelante en el período de desarrollo y pruebas, adopte un proveedor de caché de tokens distribuidos de producción.

AddDistributedMemoryCache agrega una implementación predeterminada de IDistributedCache que almacena los elementos de caché en memoria, que es utilizada por Microsoft Identity Web para el almacenamiento en caché de tokens.

AddDistributedMemoryCache requiere una referencia de paquete al Microsoft.Extensions.Caching.Memory paquete NuGet.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

Para configurar un proveedor de caché distribuida de producción, consulte Almacenamiento en caché distribuido en ASP.NET Core.

Advertencia

Reemplace siempre las memorias caché de tokens distribuidos en memoria por un proveedor de caché de tokens real al implementar la aplicación en un entorno de producción. Si no adopta un proveedor de caché de tokens distribuidos de producción, la aplicación puede sufrir un rendimiento significativamente degradado.

Para obtener más información, consulte Serialización de caché de tokens: cachés distribuidas. Sin embargo, los ejemplos de código que se muestran no se aplican a aplicaciones de ASP.NET Core, que configuran cachés distribuidas a través de AddDistributedMemoryCache, no AddDistributedTokenCache.

Use un anillo compartido de claves de protección de datos en producción para que las instancias de la aplicación a través de servidores de una granja web puedan descifrar tokens cuando MsalDistributedTokenCacheAdapterOptions.Encrypt se establece en true.

Nota

Para el desarrollo temprano y las pruebas locales en una sola máquina, puede establecer Encrypt a false y configurar un anillo compartido de claves de protección de datos más adelante:

options.Encrypt = false;

Más adelante en el período de desarrollo y pruebas, habilite el cifrado de tokens y adopte un anillo de claves de protección de datos compartido.

En el ejemplo siguiente se muestra cómo usar Azure Blob Storage y Azure Key Vault (PersistKeysToAzureBlobStorage/ProtectKeysWithAzureKeyVault) para el anillo de claves compartido. Las configuraciones de servicio son escenarios de casos base con fines de demostración. Antes de implementar aplicaciones de producción, familiarícese con los servicios de Azure y adopte procedimientos recomendados mediante sus conjuntos de documentación dedicados, que se enumeran al final de esta sección.

Agregue los siguientes paquetes al proyecto de servidor de Blazor Web App:

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

Nota

Antes de continuar con los pasos siguientes, confirme que la aplicación está registrada con Microsoft Entra.

Configure Azure Blob Storage para mantener las claves de protección de datos y cifrarlas en reposo con Azure Key Vault:

  • Cree una cuenta de almacenamiento de Azure. El nombre de la cuenta en el ejemplo siguiente es contoso.

  • Cree un contenedor para contener las claves de protección de datos. El nombre del contenedor en el ejemplo siguiente es data-protection.

  • Cree el archivo de clave en el equipo local. En el ejemplo siguiente, el archivo de clave se denomina keys.xml. Puede usar un editor de texto para crear el archivo.

    keys.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <repository>
    </repository>
    
  • Cargue el archivo de clave (keys.xml) en el contenedor de la cuenta de almacenamiento. Use el comando View/edit del menú contextual al final de la fila de claves del portal para confirmar que el blob contiene el contenido anterior.

  • Use el comando Generar SAS del menú contextual para obtener el URI del blob con una firma de acceso compartido (SAS). Al crear la SAS, use los siguientes permisos: Read, Add, Create, Write, . Delete El URI se usa más adelante donde aparece el {BLOB URI WITH SAS} marcador de posición.

Al establecer el almacén de claves en el Portal de Azure o Azure Active Directory:

  • Configure el almacén de claves para usar una política de acceso al almacén. Confirme que el acceso público en el paso Redes está habilitado (activado).

  • En el panel Directivas de acceso, cree una nueva directiva de acceso con permisos de clave Get, Unwrap Key y Wrap Key. Seleccione la aplicación registrada como principal de servicio.

  • Cuando el cifrado de claves está activo, las claves del archivo de claves incluyen el comentario "This key is encrypted with Azure Key Vault." Después de iniciar la aplicación, seleccione el comando Ver o editar en el menú contextual al final de la fila de claves para confirmar que una clave está presente con la seguridad del almacén de claves aplicada.

El AzureEventSourceLogForwarder servicio del ejemplo siguiente reenvía los mensajes de registro del SDK de Azure para el registro y requiere el Microsoft.Extensions.Azure paquete NuGet.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

En la parte superior del archivo Program, proporcione acceso a la API en el espacio de nombres Microsoft.Extensions.Azure.

using Microsoft.Extensions.Azure;

Use el código siguiente en el Program archivo donde se registran los servicios:

builder.Services.TryAddSingleton<AzureEventSourceLogForwarder>();

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI WITH SAS}"))
    .ProtectKeysWithAzureKeyVault(new Uri("{KEY IDENTIFIER}"), new DefaultAzureCredential());

{BLOB URI WITH SAS}: el URI completo donde se debe almacenar el archivo de clave con el token de SAS como parámetro de cadena de consulta. Azure Storage genera el URI cuando solicita una SAS para el archivo de clave cargado. El nombre del contenedor en el ejemplo siguiente es data-protectiony el nombre de la cuenta de almacenamiento es contoso. El archivo de clave se denomina keys.xml.

Ejemplo:

https://contoso.blob.core.windows.net/data-protection/keys.xml?sp={PERMISSIONS}&st={START DATETIME}&se={EXPIRATION DATETIME}&spr=https&sv={STORAGE VERSION DATE}&sr=c&sig={TOKEN}

{KEY IDENTIFIER}: identificador de clave de Azure Key Vault que se usa para el cifrado de claves. El nombre del almacén de claves es contoso en el ejemplo siguiente, y una directiva de acceso permite a la aplicación acceder al almacén de claves con permisos de Get, Unwrap Key, y Wrap Key. El nombre de clave de ejemplo es data-protection. La versión de la clave ({KEY VERSION} marcador de posición) se obtiene de la clave en Entra o Azure Portal después de crearla.

Ejemplo:

https://contoso.vault.azure.net/keys/data-protection/{KEY VERSION}

Inserte IDownstreamApi y llame a CallApiForUserAsync cuando actúe en nombre de un usuario.

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!");
    }
}

Las aplicaciones de ejemplo BlazorWebAppEntra y BlazorWebAppEntraBff que se describen en la sección Aplicaciones de ejemplo de este artículo usan este enfoque.

Para obtener más información, consulta los siguientes recursos:

Aplicaciones de muestra

Para ver ejemplos de trabajo, consulte las siguientes aplicaciones de ejemplo en el Blazor repositorio de GitHub de ejemplos () (dotnet/blazor-samplescómo descargar).

BlazorWebAppCallWebApi

Llamar a una API web de lista de tareas externa (no en el Blazor Web App) desde un Blazor Web App:

  • Backend: una aplicación de API web para mantener una lista de tareas pendientes, en función de las API mínimas. La aplicación de API web es una aplicación independiente de la Blazor Web App, posiblemente hospedada en otro servidor.
  • BlazorApp / BlazorApp.Client: una Blazor Web App que llama a la aplicación de API web con un HttpClient para las operaciones de lista de tareas pendientes, como crear, leer, actualizar y eliminar elementos (CRUD) de la lista de tareas pendientes.

Para la representación del lado cliente (CSR), que incluye componentes interactivos de WebAssembly y componentes automáticos que han adoptado CSR, las llamadas se realizan con un HttpClient preconfigurado registrado en el archivo Program del proyecto de cliente (BlazorApp.Client):

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

En el caso de la representación del lado servidor (SSR), que incluye componentes de servidor prerenderizados e interactivos, componentes de WebAssembly prerenderizados y componentes automáticos que son prerenderizados o han adoptado SSR, las llamadas se realizan con un HttpClient registrado en el archivo Program del proyecto del servidor (BlazorApp):

builder.Services.AddHttpClient();

Llama a una API interna de lista de películas (dentro de la Blazor Web App), donde la API reside en el proyecto de servidor de la Blazor Web App:

  • BlazorApp: una Blazor Web App que mantiene una lista de películas:
    • Cuando se realizan operaciones en la lista de películas dentro de la aplicación en el servidor, se usan llamadas API normales.
    • Cuando un cliente basado en web realiza llamadas API, se usa una API web para las operaciones de lista de películas, en función de las API mínimas.
  • BlazorApp.Client: el proyecto de cliente de la Blazor Web App, que contiene componentes interactivos de WebAssembly y Auto para la administración de usuarios de la lista de películas.

Para CSR, que incluye componentes interactivos de WebAssembly y componentes automáticos que han adoptado CSR, las llamadas a la API se realizan a través de un servicio basado en cliente (ClientMovieService) que usa un HttpClient preconfigurado registrado en el archivo Program del proyecto de cliente (BlazorApp.Client). Dado que estas llamadas se realizan a través de una web pública o privada, la API de lista de películas es una API web.

En el ejemplo siguiente se obtiene una lista de películas del endpoint /movies.

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

En el caso de SSR, que incluye componentes de servidor representados previamente e interactivos, componentes WebAssembly representados previamente y componentes automáticos representados previamente o que han adoptado SSR, las llamadas se realizan directamente a través de un servicio basado en servidor (ServerMovieService). La API no se basa en una red, por lo que es una API estándar para las operaciones CRUD de lista de películas.

En el ejemplo siguiente se obtiene una lista de películas:

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();
}

Para obtener más información sobre cómo proteger los datos de películas en este escenario, consulte el ejemplo de datos meteorológicos descrito en Protección de datos en Blazor Web Apps con renderizado automático interactivo.

BlazorWebAppCallWebApi_Weather

Una aplicación de ejemplo de datos meteorológicos que usa la representación de streaming para los datos meteorológicos.

BlazorWebAssemblyCallWebApi

Llama a una API web de lista de tareas pendientes desde una aplicación de Blazor WebAssembly :

  • Backend: una aplicación de API web para mantener una lista de tareas pendientes, en función de las API mínimas.
  • BlazorTodo: una aplicación de Blazor WebAssembly que llama a la API web con un HttpClient preconfigurado para las operaciones CRUD de lista de tareas pendientes.

BlazorWebAssemblyStandaloneWithIdentity

Una aplicación independiente Blazor WebAssembly protegida con ASP.NET Core Identity:

  • Backend: una aplicación de API web de back-end que mantiene un almacén de identidades de usuario para ASP.NET Core Identity.
  • BlazorWasmAuth: una aplicación de front-end Blazor WebAssembly independiente con autenticación de usuario.

La solución muestra cómo llamar a una API web segura para lo siguiente:

  • Obtener los roles de un usuario autenticado.
  • Procesamiento de datos para todos los usuarios autenticados.
  • Procesamiento de datos para usuarios autorizados (el usuario debe estar en el Manager rol) a través de una directiva de autorización.

BlazorWebAppOidc

Una interactividad automática global con Blazor Web App que utiliza la autenticación de OIDC con Microsoft Entra sin usar paquetes específicos de Entra. En el ejemplo se muestra cómo usar un controlador de tokens para las llamadas API web para llamar a una API web segura externa.

BlazorWebAppOidcServer

Una interactividad global del Interactive Server que utiliza autenticación OIDC con Microsoft Entra sin emplear paquetes específicos de Entra. En el ejemplo se muestra cómo pasar un token de acceso para llamar a una API web segura externa.

BlazorWebAppOidcBff

Una interactividad automática global con Blazor Web App que usa:

  • Autenticación OIDC con Microsoft Entra sin usar paquetes específicos de Entra.
  • El patrón Backend for Frontend (BFF), que es un patrón de desarrollo de aplicaciones que crea servicios posteriores para aplicaciones o interfaces frontales.

La solución incluye una demostración de cómo obtener datos meteorológicos de forma segura a través de una API web externa cuando un componente que adopta la representación automática interactiva se representa en el cliente.

BlazorWebAppEntra

Interactividad global de Auto con Blazor Web App que utiliza la plataforma de identidad de Microsoft con paquetes web de Microsoft Identity para Microsoft Entra ID. La solución incluye una demostración de cómo obtener datos meteorológicos de forma segura a través de una API web externa cuando un componente que adopta la representación automática interactiva se representa en el cliente.

BlazorWebAppEntraBff

Una interactividad automática global con Blazor Web App que usa:

La solución incluye una demostración de cómo obtener datos meteorológicos de forma segura a través de una API web externa cuando un componente que adopta la representación automática interactiva se representa en el cliente.

Eliminación de HttpRequestMessage, HttpResponseMessagey HttpClient

Un HttpRequestMessage sin cuerpo no requiere eliminación explícita con una using declaración (C# 8 o posterior) o un using bloque (todas las versiones de C#), pero se recomienda eliminar con cada uso por los siguientes motivos:

  • Para obtener una mejora en el rendimiento evitando los finalizadores.
  • Fortalece el código para el futuro en caso de que alguna vez se agregue un cuerpo de solicitud a un HttpRequestMessage que inicialmente no tenía uno.
  • Para evitar potencialmente problemas funcionales si un controlador de delegación espera una llamada a Dispose/DisposeAsync.
  • Es más sencillo aplicar una regla general en todas partes que intentar recordar casos específicos.

Elimine siempre las HttpResponseMessage instancias.

Nunca elimine HttpClient las instancias creadas mediante una llamada a CreateClient porque están administradas por el marco.

Ejemplo:

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

Escenarios del lado cliente para llamar a API web externas

Los componentes basados en cliente llaman a APIs web externas usando instancias de HttpClient, que normalmente se crean con un HttpClient preconfigurado registrado en el archivo Program:

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

El siguiente Razor componente realiza una solicitud a una API web para ramas de GitHub similares al ejemplo de uso básico en el artículo Realización de solicitudes HTTP mediante IHttpClientFactory en 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; }
    }
}

En el ejemplo anterior de C# 12 o posterior, se crea una matriz vacía ([]) para la variable branches. Para versiones anteriores de C# compiladas con un SDK anterior a .NET 8, cree una matriz vacía (Array.Empty<GitHubBranch>()).

Para proteger el código y los datos de .NET/C#, use las características de Protección de Datos de ASP.NET Core con una API web del lado del servidor ASP.NET Core. La aplicación Blazor WebAssembly del lado cliente llama a la API web del lado servidor para proteger las características de la aplicación y el procesamiento de datos.

Blazor WebAssembly A menudo se impide que las aplicaciones realicen llamadas directas entre orígenes a las API web debido a la seguridad CORS (Compartición de Recursos entre Orígenes). Una excepción típica es similar a la siguiente:

El acceso a la obtención en '{URL}' desde el origen 'https://localhost:{PORT}' ha sido bloqueado por la directiva de CORS: no hay encabezado 'Access-Control-Allow-Origin' presente en el recurso solicitado. Si una respuesta opaca satisface sus necesidades, establezca el modo de la solicitud en 'no-cors' para obtener el recurso con CORS deshabilitado.

Incluso si llama a SetBrowserRequestMode con un campo BrowserRequestMode de NoCors (1) que busca eludir la excepción anterior, la solicitud suele fallar debido a las restricciones de CORS en el origen de la API web, como una restricción que solo permite llamadas desde orígenes específicos o una restricción que impide las solicitudes JavaScript fetch desde un navegador. La única manera de que estas llamadas se realicen correctamente es que la API web que estás llamando permita que tu origen llame a su propio origen con la configuración de CORS correcta. La mayoría de las API web externas no permiten configurar sus directivas de CORS. Para tratar esta restricción, adopte cualquiera de las siguientes estrategias:

  • Mantenga su propia API web de back-end del lado servidor de ASP.NET Core. La aplicación del lado cliente Blazor WebAssembly llama a la API web del lado servidor y la API web realiza la solicitud desde su código de C# basado en servidor (no un explorador) a la API web externa con los encabezados CORS correctos, devolviendo el resultado a la aplicación de Blazor WebAssembly del lado cliente.

  • Usar un servicio de proxy para reenviar la solicitud de la aplicación Blazor WebAssembly del lado cliente a la API web externa. El servicio proxy usa una aplicación del lado servidor para realizar la solicitud en nombre del cliente y devuelve el resultado después de que la llamada se realice correctamente. En el siguiente ejemplo basado en el proxy CORS de CloudFlare , el marcador de posición {REQUEST URI} es el URI de solicitud.

    @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);
    
            ...
        }
    }
    

Escenarios del lado servidor para llamar a API web externas

Los componentes basados en servidor llaman a las API web externas usando instancias de HttpClient, normalmente creadas usando IHttpClientFactory. Para obtener instrucciones que se aplican a las aplicaciones del lado servidor, consulte Realización de solicitudes HTTP con IHttpClientFactory en ASP.NET Core.

Una aplicación de servidor no incluye un servicio HttpClient. Proporcione un HttpClient a la aplicación usando la infraestructura de fábrica de HttpClient.

En el archivo Program:

builder.Services.AddHttpClient();

El siguiente Razor componente realiza una solicitud a una API web para ramas de GitHub similares al ejemplo de uso básico en el artículo Realización de solicitudes HTTP mediante IHttpClientFactory en 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; }
    }
}

En el ejemplo anterior de C# 12 o posterior, se crea una matriz vacía ([]) para la variable branches. Para versiones anteriores de C# compiladas con un SDK anterior a .NET 8, cree una matriz vacía (Array.Empty<GitHubBranch>()).

Para obtener un ejemplo de trabajo adicional, consulte el ejemplo de carga de archivos del lado servidor que carga archivos en un controlador de API web en el artículo sobre cargas de archivos de ASP.NET CoreBlazor.

Abstracciones de servicio para llamadas API web

Esta sección se aplica a Blazor Web Apps que mantienen una API web en el proyecto de servidor o transforman llamadas API web a una API web externa.

Al usar los modos de representación interactiva de WebAssembly y Auto, los componentes se prerenderizan por defecto. Los componentes automáticos también se representan inicialmente de forma interactiva desde el servidor antes de que la agrupación de Blazor se descargue en el cliente y el entorno de ejecución del lado cliente se active. Esto significa que los componentes que usan estos modos de representación deben diseñarse para que se ejecuten correctamente desde el cliente y el servidor. Si el componente debe llamar a una API basada en proyectos de servidor o transformar una solicitud a una API web externa (una que está fuera de la Blazor Web App) al ejecutarse en el cliente, el enfoque recomendado es abstraer esa llamada API detrás de una interfaz de servicio e implementar versiones de cliente y servidor del servicio:

  • La versión del cliente llama a la API web con un HttpClient preconfigurado.
  • Normalmente, la versión del servidor puede acceder directamente a los recursos del lado servidor. La inserción de un HttpClient en el servidor que realiza llamadas de vuelta al servidor no se recomienda, ya que la solicitud de red suele ser innecesaria. Como alternativa, la API podría ser externa al proyecto de servidor, pero se requiere una abstracción de servicio para el servidor para transformar la solicitud de alguna manera, por ejemplo, para agregar un token de acceso a una solicitud proxy.

Al usar el modo de representación de WebAssembly, también tienes la opción de deshabilitar la representación previa, por lo que los componentes solo se representan desde el cliente. Para obtener más información, consulte modos de representación de ASP.NET CoreBlazor.

Ejemplos (aplicaciones de ejemplo):

  • API web de lista de películas en la aplicación de ejemplo de BlazorWebAppCallWebApi.
  • Streaming que representa los datos meteorológicos de la API web en la aplicación de BlazorWebAppCallWebApi_Weather muestra.
  • Los datos meteorológicos se devuelven al cliente en las aplicaciones de ejemplo utilizando el BlazorWebAppOidc (patrón no BFF) o el BlazorWebAppOidcBff (patrón BFF). Estas aplicaciones muestran llamadas API seguras (web). Para obtener más información, consulta Blazor Web App.

API web externas de Blazor Web App

Blazor Web App

Las Blazor Web App normalmente representan los componentes de WebAssembly del lado cliente y los componentes automáticos se representan en el servidor durante la representación estática o interactiva del lado servidor (SSR). Los servicios HttpClient no se registran de forma predeterminada en el proyecto principal de un Blazor Web App. Si la aplicación se ejecuta solo con los HttpClient servicios registrados en el .Client proyecto, como se describe en la sección Agregar el HttpClient servicio , la ejecución de la aplicación produce un error en tiempo de ejecución:

InvalidOperationException: No se puede proporcionar un valor para la propiedad "Http" en el tipo "... {COMPONENT}'. No hay ningún servicio registrado de tipo "System.Net.Http.HttpClient".

Use cualquiera de los métodos siguientes:

  • Agregue los servicios HttpClient al proyecto principal para que HttpClient esté disponible durante el SSR. Usa el siguiente registro de servicio en el archivo Program del servidor principal:

    builder.Services.AddHttpClient();
    

    El marco compartido proporciona servicios HttpClient, por lo que no se requiere una referencia de paquete en el archivo de proyecto de la aplicación.

    Ejemplo: API web de lista de tareas en la BlazorWebAppCallWebApiaplicación de muestra

  • Si la representación previa no es necesaria para un componente de WebAssembly que llama a la API web, deshabilite la representación previa siguiendo las instrucciones de ASP.NET modos de representación de CoreBlazor. Si adoptas este enfoque, no es necesario agregar servicios HttpClient al proyecto principal de la Blazor Web App porque el componente no se representará previamente en el servidor.

Para obtener más información, consulte Los servicios del lado cliente no se resuelven durante la representación previa.

Datos representados previamente

Al representar previamente, los componentes se representan dos veces: primero estáticamente y, después, de forma interactiva. El estado no fluye automáticamente desde el componente creado previamente hasta el interactivo. Si un componente realiza operaciones de inicialización asíncronas y representa diferentes contenidos para diferentes estados durante la inicialización, como un indicador de progreso "Cargando...", puedes ver un parpadeo cuando el componente se representa dos veces.

Para solucionarlo, puede abordar el estado prerenderizado al usar la API de Estado de Componente Persistente, lo cual se demuestra con las aplicaciones de ejemplo y . Cuando el componente se representa de forma interactiva, puede representarse de la misma manera con el mismo estado. Sin embargo, la API no funciona actualmente con la navegación mejorada, algo que puedes solucionar deshabilitando la navegación mejorada en los vínculos a la página (data-enhanced-nav=false). Para obtener más información, consulta los siguientes recursos:

Streaming de solicitudes del lado cliente

En el caso de los exploradores basados en Chromium (por ejemplo, Google Chrome y Microsoft Edge) mediante el protocolo HTTP/2 y HTTPS, el lado Blazor cliente usa Streams API para permitir el streaming de solicitudes.

Para habilitar el streaming de solicitudes, establezca SetBrowserRequestStreamingEnabled a true en el HttpRequestMessage.

En el ejemplo de carga de archivos siguiente:

  • content es el HttpContentdel archivo .
  • /Filesave es el punto de conexión de la API web.
  • Http es el HttpClient.
using var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
request.SetBrowserRequestStreamingEnabled(true);
request.Content = content;

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

Solicitudes de streaming:

  • Requiere el protocolo HTTPS y no funciona en HTTP/1.x.
  • Incluya un cuerpo pero no un encabezado Content-Length. CORS con una solicitud preparatoria es necesaria para las solicitudes de streaming entre orígenes.

Para obtener más información sobre las cargas de archivos con InputFile component, consulte ASP.NET Core Blazor cargas de archivos y el ejemplo en Carga de archivos en un servidor con representación del lado cliente (CSR).

Añadir el servicio HttpClient

Las instrucciones de esta sección se aplican a los escenarios del lado cliente.

Los componentes del lado del cliente llaman a las API web utilizando un servicio preconfigurado HttpClient, que se centra en realizar peticiones de vuelta al servidor de origen. Se pueden crear configuraciones del servicio HttpClient adicionales para otras API web en el código de desarrollo. Las solicitudes se crean mediante Blazorasistentes JSON o con HttpRequestMessage. Las solicitudes pueden incluir la configuración de la opción Fetch API .

Los ejemplos de configuración de esta sección solo son útiles cuando se llama a una sola API web para una sola instancia HttpClient de la aplicación. Cuando la aplicación debe llamar a varias API web, cada una con su propia dirección base y configuración, puede adoptar los siguientes enfoques, que se tratan más adelante en este artículo:

  • Nombrado HttpClient con IHttpClientFactory: cada API web recibe un nombre único. Cuando el código de la aplicación o un componente Razor llama a una API web, usa una instancia denominada HttpClient para realizar la llamada.
  • HttpClientCon tipo: cada API web está tipificada. Cuando el código de la aplicación o un componente Razor llama a una API web, usa una instancia HttpClient con tipo para realizar la llamada.

En el archivo Program, añada un servicio HttpClient si no está ya presente desde una plantilla de proyecto Blazor utilizada para crear la aplicación:

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

En el ejemplo anterior se establece la dirección base con builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), que obtiene la dirección base de la aplicación y se deriva normalmente del valor <base> de la etiqueta href en la página host.

Los casos de uso más comunes para usar la dirección base del cliente son:

  • El proyecto de cliente (.Client) de una Blazor Web App (.NET 8 o posterior) realiza llamadas a API web desde componentes o código de WebAssembly que se ejecuta en el cliente de WebAssembly a las API de la aplicación de servidor.
  • El proyecto de cliente (Client) de una aplicación hospedada Blazor WebAssembly realiza llamadas API web al proyecto de servidor (Server). Tenga en cuenta que la plantilla de proyecto hospedada Blazor WebAssembly ya no está disponible en .NET 8 o posterior. Sin embargo, las aplicaciones hospedadas Blazor WebAssembly siguen siendo compatibles con .NET 8.

Si llamas a una API web externa (que no se encuentra en el mismo espacio de direcciones URL que la aplicación cliente), establece el URI en la dirección base de la API web. En el siguiente ejemplo se establece la dirección base de la API web en https://localhost:5001, donde se está ejecutando una aplicación de API web independiente y lista para responder a las solicitudes de la aplicación cliente:

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

Asistentes de JSON

HttpClient está disponible como un servicio preconfigurado para realizar solicitudes de vuelta al servidor de origen.

HttpClient y los asistentes de JSON (System.Net.Http.Json.HttpClientJsonExtensions) también se usan para llamar a puntos de conexión de API web de terceros. HttpClientse implementa mediante la API Fetch del explorador y está sujeta a sus limitaciones, incluida la aplicación de la directiva de mismo origen, que se describe más adelante en este artículo en la sección Uso compartido de recursos entre orígenes (CORS).

La dirección base del cliente se establece en la dirección del servidor de origen. Inserta una instancia de HttpClient en un componente con la directiva @inject:

@using System.Net.Http
@inject HttpClient Http

Usa el espacio de nombres System.Net.Http.Json para acceder a HttpClientJsonExtensions, incluidos GetFromJsonAsync, PutAsJsonAsync y PostAsJsonAsync:

@using System.Net.Http.Json

En las secciones siguientes se tratan las funciones auxiliares JSON.

System.Net.Http incluye métodos adicionales para enviar solicitudes HTTP y recibir respuestas HTTP, por ejemplo, para enviar una solicitud DELETE. Para obtener más información, consulte la sección DELETE y métodos de extensión adicionales .

GET de JSON (GetFromJsonAsync)

GetFromJsonAsync envía una solicitud HTTP GET y analiza el cuerpo de la respuesta JSON para crear un objeto.

En el código de componente siguiente, el componente muestra el elemento todoItems. Se llama a GetFromJsonAsync cuando el componente termina de inicializarse (OnInitializedAsync).

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

POST como JSON (PostAsJsonAsync)

PostAsJsonAsync envía una solicitud POST al URI especificado que contiene el valor serializado como JSON en el cuerpo de la solicitud.

En el siguiente código de componente, newItemName es proporcionado por un elemento enlazado del componente. El método AddItem se desencadena al seleccionar un elemento <button>.

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

PostAsJsonAsync devuelve un valor de HttpResponseMessage. Para deserializar el contenido JSON del mensaje de respuesta, usa el método de extensión ReadFromJsonAsync. En el ejemplo siguiente se leen los datos meteorológicos JSON como una matriz:

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

POST como JSON (PutAsJsonAsync)

PutAsJsonAsync envía una solicitud HTTP PUT con el contenido con codificación JSON.

En el código de componente siguiente, los elementos enlazados del componente proporcionan valores editItem para Name e IsCompleted. El valor Id del elemento se establece cuando el elemento está seleccionado en otra parte de la interfaz de usuario (no se muestra) y se llama a EditItem. El método SaveItem se desencadena al seleccionar el elemento <button>. En el ejemplo siguiente no se muestra la carga de todoItems por motivos de brevedad. Consulte la sección GET de JSON (GetFromJsonAsync) para obtener un ejemplo de carga de elementos.

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

PutAsJsonAsync devuelve un valor de HttpResponseMessage. Para deserializar el contenido JSON del mensaje de respuesta, usa el método de extensión ReadFromJsonAsync. En el ejemplo siguiente se leen los datos meteorológicos JSON como una matriz:

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

PATCH como JSON (PatchAsJsonAsync)

PatchAsJsonAsync envía una solicitud HTTP PATCH con el contenido con codificación JSON.

Nota

Para obtener más información, consulte JsonPatch en ASP.NET CORE web API.

En el siguiente ejemplo, PatchAsJsonAsync recibe un documento JSON PATCH como una cadena de texto sin formato con comillas escapadas:

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

A partir de C# 11 (.NET 7), puedes componer una cadena JSON como un literal de cadena sin procesar. Especifique la sintaxis JSON con el StringSyntaxAttribute.Json campo al [StringSyntax] atributo para las herramientas de análisis de código:

@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 devuelve un valor de HttpResponseMessage. Para deserializar el contenido JSON del mensaje de respuesta, usa el método de extensión ReadFromJsonAsync. En el ejemplo siguiente se leen los datos del elemento de tareas pendientes JSON como una matriz. Se crea una matriz vacía si el método no devuelve ningún dato de elemento, por lo que content no es nulo después de que se ejecuta la instrucción:

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

Dispuesto con sangría, espaciado y comillas sin mayúsculas, el documento PATCH sin codificar aparece como el siguiente JSON:

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

Para simplificar la creación de documentos PATCH en la aplicación que emite solicitudes PATCH, una aplicación puede usar la compatibilidad con .NET JSON PATCH, como se muestra en las instrucciones siguientes.

Instala el paquete NuGet Microsoft.AspNetCore.JsonPatch.SystemTextJson y usa las características de API del paquete para crear un JsonPatchDocument para una solicitud PATCH.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

Agregue directivas @using para los espacios de nombres System.Text.Json, System.Text.Json.Serialization, y Microsoft.AspNetCore.JsonPatch.SystemTextJson en la parte superior del componente Razor.

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

Cree el JsonPatchDocument para un objeto TodoItem establecido en IsCompletetrue mediante el método JsonPatchDocument.Replace:

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

Instala el paquete NuGet Microsoft.AspNetCore.JsonPatch y usa las características de API del paquete para crear un JsonPatchDocument para una solicitud PATCH.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

Agrega directivas @using para los espacios de nombres System.Text.Json, System.Text.Json.Serialization y Microsoft.AspNetCore.JsonPatch en la parte superior del componente Razor :

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

Cree el JsonPatchDocument para un objeto TodoItem establecido en IsCompletetrue mediante el método Replace:

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

Pase las operaciones del documento (patchDocument.Operations) a la llamada PatchAsJsonAsync:

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

JsonSerializerOptions.DefaultIgnoreCondition se establece como JsonIgnoreCondition.WhenWritingDefault para omitir una propiedad solo si es igual al valor predeterminado de su tipo.

Añada JsonSerializerOptions.WriteIndented ajustado a true si desea presentar la carga útil JSON en un formato agradable para su visualización. Escribir JSON sangrado no tiene ninguna relación con el procesamiento de solicitudes PATCH y no se suele realizar en aplicaciones de producción para solicitudes de API web.

Siga las instrucciones del artículo JsonPatch en ASP.NET API web core para agregar una acción del controlador PATCH a la API web. Como alternativa, el procesamiento de solicitudes PATCH se puede implementar como una API mínima con los pasos siguientes.

Agrega una referencia de paquete para el paquete NuGet Microsoft.AspNetCore.JsonPatch.SystemTextJson a la aplicación de API web.

En el Program archivo agregue una @using directiva para el Microsoft.AspNetCore.JsonPatch.SystemTextJson espacio de nombres.

using Microsoft.AspNetCore.JsonPatch.SystemTextJson;

Agrega una referencia de paquete para el paquete NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson a la aplicación de API web.

Nota

No es necesario agregar una referencia de paquete para el paquete Microsoft.AspNetCore.JsonPatch a la aplicación porque la referencia al paquete Microsoft.AspNetCore.Mvc.NewtonsoftJson agrega automáticamente una referencia de paquete para Microsoft.AspNetCore.JsonPatch.

En el archivo Program, agrega una directiva @using para el espacio de nombres Microsoft.AspNetCore.JsonPatch:

using Microsoft.AspNetCore.JsonPatch;

Proporciona el punto de conexión a la canalización de procesamiento de solicitudes de la 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();
});

Advertencia

Al igual que con los otros ejemplos del JsonPatch en el artículo de API web de ASP.NET Core, la API PATCH anterior no protege la API web frente a ataques de sobrecarga de publicación. Para obtener más información, consulte Tutorial: Creación de una API web basada en controlador con ASP.NET Core.

Para obtener una experiencia PATCH totalmente funcional, consulte la BlazorWebAppCallWebApiaplicación de ejemplo.

DELETE (DeleteAsync) y métodos de extensión adicionales

System.Net.Http incluye métodos de extensión adicionales para enviar solicitudes HTTP y recibir respuestas HTTP. HttpClient.DeleteAsync se usa para enviar una solicitud HTTP DELETE a una API web.

En el código de componente siguiente, el elemento <button> llama al método DeleteItem. El elemento <input> enlazado proporciona el valor id del elemento que se va a eliminar.

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

Denominado HttpClient con IHttpClientFactory

Se admiten los servicios IHttpClientFactory y la configuración de un HttpClient con nombre.

Nota

Una alternativa al uso de un HttpClient con nombre a partir de un IHttpClientFactory es utilizar un HttpClient con tipo. Para obtener más información, consulte la sección Con tipo HttpClient .

Agrega el paquete NuGet Microsoft.Extensions.Http a la aplicación.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

En el archivo Program de un proyecto de cliente:

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

Si el cliente nombrado va a ser utilizado por componentes prerenderizados del lado del cliente de un Blazor Web App, el registro de servicio precedente debe aparecer tanto en el proyecto del servidor como en el proyecto .Client. En el servidor, builder.HostEnvironment.BaseAddress se reemplaza por la dirección base de la API web, que se describe más adelante.

En el ejemplo anterior del lado cliente se establece la dirección base con builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), que obtiene la dirección base de la aplicación del lado cliente y se deriva normalmente del valor <base> de la etiqueta href en la página host.

Los casos de uso más comunes para usar la dirección base del cliente son:

  • El proyecto de cliente (.Client) de una aplicación web Blazor Web App que realiza llamadas a la API web desde componentes WebAssembly/Auto o código que se ejecuta en el cliente en WebAssembly a las API de la aplicación del servidor en la misma dirección de host.
  • El proyecto cliente (Client) de una aplicación Blazor WebAssembly hospedada que realiza llamadas API web al proyecto de servidor (Server).

El caso de uso más común para usar la propia dirección base del cliente está en el proyecto de cliente (Client) de una aplicación hospedada Blazor WebAssembly que realiza llamadas API web al proyecto de servidor (Server).

Si llamas a una API web externa (no en el mismo espacio de direcciones URL que la aplicación cliente) o configuras los servicios en una aplicación del lado servidor (por ejemplo, para tratar la representación previa de componentes del lado cliente en el servidor), establece el URI en la dirección base de la API web. En el siguiente ejemplo se establece la dirección base de la API web en https://localhost:5001, donde se está ejecutando una aplicación de API web independiente y lista para responder a las solicitudes de la aplicación cliente:

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

En el siguiente código de componente:

  • Una instancia de IHttpClientFactory crea un HttpClient con nombre.
  • El elemento denominado HttpClient se usa para realizar una solicitud GET para obtener datos JSON de previsión meteorológica desde la API web en /forecast.
@inject IHttpClientFactory ClientFactory

...

@code {
    private Forecast[]? forecasts;

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

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

La BlazorWebAppCallWebApiaplicación de ejemplo muestra cómo llamar a una API web con un nombre HttpClient en su CallTodoWebApiCsrNamedClient componente. Para obtener una demostración de trabajo adicional en una aplicación cliente basada en llamar a Microsoft Graph con un denominado HttpClient, consulte Uso de Graph API con ASP.NET Core Blazor WebAssembly.

Para obtener una demostración de trabajo en una aplicación cliente basada en llamar a Microsoft Graph con un denominado HttpClient, consulte Uso de Graph API con ASP.NET Core Blazor WebAssembly.

Con tipo HttpClient

Typed HttpClient utiliza una o varias instancias de HttpClient de la aplicación, ya sean predeterminadas o con nombre, para devolver datos de uno o varios puntos de conexión de la API web.

Nota

Una alternativa al uso de un objeto HttpClient con tipo es usar un HttpClient con nombre desde IHttpClientFactory. Para obtener más información, consulte la sección DenominadaHttpClientIHttpClientFactory.

Agrega el paquete NuGet Microsoft.Extensions.Http a la aplicación.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos en Instalación y administración de paquetes en el flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirme las versiones correctas del paquete en NuGet.org.

El siguiente ejemplo emite una solicitud GET para datos de pronóstico del tiempo JSON desde la API web en /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") ?? [];
}

En el archivo Program de un proyecto de cliente:

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

Si el cliente nombrado va a ser utilizado por componentes prerenderizados del lado del cliente de un Blazor Web App, el registro de servicio precedente debe aparecer tanto en el proyecto del servidor como en el proyecto .Client. En el servidor, builder.HostEnvironment.BaseAddress se reemplaza por la dirección base de la API web, que se describe más adelante.

En el ejemplo anterior se establece la dirección base con builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), que obtiene la dirección base de la aplicación del lado cliente y se deriva normalmente del valor <base> de la etiqueta href en la página host.

Los casos de uso más comunes para usar la dirección base del cliente son:

  • El proyecto de cliente (.Client) de una aplicación web Blazor Web App que realiza llamadas a la API web desde componentes WebAssembly/Auto o código que se ejecuta en el cliente en WebAssembly a las API de la aplicación del servidor en la misma dirección de host.
  • El proyecto cliente (Client) de una aplicación Blazor WebAssembly hospedada que realiza llamadas API web al proyecto de servidor (Server).

El caso de uso más común para usar la propia dirección base del cliente está en el proyecto de cliente (Client) de una aplicación hospedada Blazor WebAssembly que realiza llamadas API web al proyecto de servidor (Server).

Si llamas a una API web externa (no en el mismo espacio de direcciones URL que la aplicación cliente) o configuras los servicios en una aplicación del lado servidor (por ejemplo, para tratar la representación previa de componentes del lado cliente en el servidor), establece el URI en la dirección base de la API web. En el siguiente ejemplo se establece la dirección base de la API web en https://localhost:5001, donde se está ejecutando una aplicación de API web independiente y lista para responder a las solicitudes de la aplicación cliente:

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

Los componentes insertan la instancia de HttpClient con tipo para llamar a la API web.

En el siguiente código de componente:

  • Se inserta una instancia de la clase ForecastHttpClient anterior, que crea un objeto con tipo HttpClient.
  • El tipo HttpClient se usa para emitir una solicitud GET para obtener datos JSON de previsión meteorológica desde la API web.
@inject ForecastHttpClient Http

...

@code {
    private Forecast[]? forecasts;

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

La BlazorWebAppCallWebApiaplicación de ejemplo muestra cómo llamar a una API web con un HttpClient tipado en su componente CallTodoWebApiCsrTypedClient. Tenga en cuenta que el componente adopta la representación del lado del cliente (CSR) (InteractiveWebAssembly modo de representación) con prerenderización, de modo que el registro del servicio cliente tipado aparece en el archivo Program del proyecto del servidor y el proyecto .Client.

La guía de esta sección se aplica a los escenarios del lado cliente que se basan en una autenticación cookie.

Para la autenticación basada en cookie, que se considera más segura que la autenticación de token de portador, las credenciales cookie se pueden enviar con cada solicitud de API web llamando a AddHttpMessageHandler con un DelegatingHandler en un HttpClient preconfigurado. El controlador configura SetBrowserRequestCredentials con BrowserRequestCredentials.Include, que aconseja al explorador que envíe credenciales con cada solicitud, como cookies o encabezados de autenticación HTTP, incluso para solicitudes entre orígenes.

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

Para obtener instrucciones sobre cómo acceder a un AuthenticationStateProvider desde un DelegatingHandler, consulte ASP.NET Core del lado del servidor y escenarios de seguridad adicionalesBlazor Web App.

CookieHandler se registra en el archivo Program:

builder.Services.AddTransient<CookieHandler>();

El controlador de mensajes se agrega a cualquier HttpClient preconfigurado que requiera autenticación cookie:

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

Para obtener una demostración, consulte Protección de ASP.NET Core Blazor WebAssembly con ASP.NET Core Identity.

Al redactar un HttpRequestMessage, establece las credenciales de solicitud del explorador y el encabezado directamente:

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

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

HttpClient y HttpRequestMessage con las opciones de solicitud de Fetch API

La guía de esta sección se aplica a los escenarios del lado cliente que dependen de la autenticación de tokens de portador.

HttpClient (documentación de API) y HttpRequestMessage se puede usar para personalizar las solicitudes. Por ejemplo, se puede especificar el método HTTP y los encabezados de solicitud. El siguiente componente realiza una solicitud POST a un punto de conexión de API web y muestra el cuerpo de la respuesta.

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; }
    }
}

BlazorLa implementación en el lado del cliente de HttpClient utiliza Fetch API y configura las opciones de Fetch API específicas de la solicitud mediante HttpRequestMessage métodos de extensión y WebAssemblyHttpRequestMessageExtensions. Establece más opciones usando el método de extensión SetBrowserRequestOption genérico. Blazor y la API Fetch subyacente no agregan ni modifican directamente los encabezados de solicitud. Para obtener más información sobre cómo los agentes de usuario, como los exploradores, interactúan con los encabezados, consulta los conjuntos de documentación del agente de usuario externo y otros recursos web.

El streaming de respuesta está habilitado de forma predeterminada.

La llamada a HttpContent.ReadAsStreamAsync para un HttpResponseMessage.Content (response.Content.ReadAsStreamAsync()) devuelve un BrowserHttpReadStream (origen de referencia), no un MemoryStream. BrowserHttpReadStream no admite operaciones sincrónicas, como Stream.Read(Span<Byte>). Si su código utiliza operaciones sincrónicas, puede optar por no participar en la transmisión de respuesta o copiar el Stream en el MemoryStream usted mismo.

Nota

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta para una versión específica, use la lista desplegable Cambiar ramas o etiquetas . Para obtener más información, vea Cómo seleccionar una etiqueta de versión de ASP.NET código fuente de Core (dotnet/AspNetCore.Docs #26205).

Para no participar en el streaming de respuesta globalmente, use cualquiera de los enfoques siguientes:

  • Agregue la <WasmEnableStreamingResponse> propiedad al archivo de proyecto con un valor de false:

    <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>
    
  • Establezca la variable de DOTNET_WASM_ENABLE_STREAMING_RESPONSE entorno en false o 0.

Para excluirse del streaming de respuesta para una solicitud individual, establezca SetBrowserResponseStreamingEnabled en false en HttpRequestMessage (request en el ejemplo siguiente):

request.SetBrowserResponseStreamingEnabled(false);

Normalmente, la respuesta HTTP se almacena en búfer para habilitar la compatibilidad con las lecturas sincrónicas en el contenido de la respuesta. Para habilitar la compatibilidad con el streaming de respuesta, establezca SetBrowserResponseStreamingEnabled en true en HttpRequestMessage:

request.SetBrowserResponseStreamingEnabled(true);

De forma predeterminada, se establece HttpCompletionOption.ResponseContentRead, lo que da como resultado que HttpClient complete después de leer toda la respuesta, incluido el contenido. Para poder usar la SetBrowserResponseStreamingEnabled opción en archivos grandes, establezca HttpCompletionOption.ResponseHeadersRead para evitar almacenar en caché el contenido del archivo en memoria:

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

Para incluir credenciales en una solicitud entre orígenes, usa el método de extensión SetBrowserRequestCredentials:

request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

Para obtener más información sobre las opciones de Fetch API, consulte documentos web de MDN: WindowOrWorkerGlobalScope.fetch(): Parameters.

Manejo de errores

Controla los errores de respuesta de la API web en el código de desarrollo cuando se produzcan. Por ejemplo, GetFromJsonAsync espera una respuesta JSON de la API web con un Content-Type de application/json. Si la respuesta no está en formato JSON, la validación del contenido producirá una excepción NotSupportedException.

En el ejemplo siguiente, el punto de conexión del URI para la solicitud de datos de previsión meteorológica está mal escrito. El URI debe ser WeatherForecast, pero aparece en la llamada como WeatherForcast, y le falta la letra e en Forecast.

La llamada a GetFromJsonAsync espera que se devuelva JSON, pero la API web devuelve HTML para una excepción no controlada con un Content-Type de text/html. La excepción no controlada se produce porque no se encuentra la ruta de acceso a /WeatherForcast y el middleware no puede proporcionar una página o vista para la solicitud.

En OnInitializedAsync en el cliente, se produce una excepción NotSupportedException cuando el contenido de la respuesta se valida como distinto de JSON. La excepción se captura en el bloque catch, donde la lógica personalizada podría registrar el error o mostrar al usuario un mensaje de error descriptivo:

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

El ejemplo anterior sirve de demostración. Se puede configurar una API web para que devuelva JSON incluso cuando no existe un punto de conexión o cuando se produce una excepción no controlada en el servidor.

Para obtener más información, consulte Control de errores en aplicaciones de ASP.NET CoreBlazor.

Uso compartido de recursos entre orígenes (CORS)

La seguridad del explorador suele restringir que una página web realice solicitudes a un origen diferente al que actuó en la página web. Esta restricción se denomina directiva de mismo origen. La directiva de mismo origen restringe, pero no impide, que un sitio malintencionado lea información confidencial de otro sitio. Para realizar solicitudes desde el explorador a un punto de conexión con un origen diferente, el punto de conexión debe habilitar el uso compartido de recursos entre orígenes (CORS).

Para obtener más información sobre CORS del lado servidor, vea Habilitar solicitudes entre orígenes (CORS) en ASP.NET Core. Los ejemplos del artículo no pertenecen directamente a los escenarios de los componentes Razor, pero el artículo es útil para aprender conceptos generales de CORS.

Para obtener información sobre las solicitudes CORS del lado cliente, consulte ASP.NET Escenarios de seguridad adicionales de CoreBlazor WebAssembly.

Soporte para la prevención de falsificaciones

Para agregar compatibilidad antiforgería a una solicitud HTTP, inserta AntiforgeryStateProvider y agrega un RequestToken a la colección de encabezados como un 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);
    ...
}

Para obtener más información, consulte ASP.NET Core Blazor autenticación y autorización.

Ejemplos de componentes del marco Blazor para probar el acceso a la API web

Hay varias herramientas de red disponibles públicamente para probar aplicaciones back-end de API web directamente, como Firefox Browser Developer. El origen de referencia del marco Blazor incluye recursos de prueba HttpClient que son útiles para las pruebas:

HttpClientTest recursos en el repositorio de dotnet/aspnetcore GitHub

Nota

Los vínculos de la documentación al origen de referencia de .NET cargan normalmente la rama predeterminada del repositorio, que representa el desarrollo actual para la próxima versión de .NET. Para seleccionar una etiqueta para una versión específica, use la lista desplegable Cambiar ramas o etiquetas . Para obtener más información, vea Cómo seleccionar una etiqueta de versión de ASP.NET código fuente de Core (dotnet/AspNetCore.Docs #26205).

Recursos adicionales

General

Mitigación de ataques por publicación excesiva

Las API web pueden ser vulnerables a un ataque de sobrepostificación , también conocido como ataque de asignación masiva . Un ataque por publicación excesiva se produce cuando un usuario malintencionado emite una instrucción POST de formulario HTML en el servidor que procesa los datos de las propiedades que no forman parte del formulario representado y que el desarrollador no desea permitir que los usuarios modifiquen. El término "overposting" significa literalmente que el usuario malintencionado ha realizado más solicitudes POST de las necesarias con el formulario.

Para obtener instrucciones sobre la mitigación de ataques de sobrepostificación, consulte Tutorial: Creación de una API web basada en controlador con ASP.NET Core.

Lado servidor

Lado cliente