Compartir vía


Llamada a una API web desde Blazor de ASP.NET Core

Nota

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

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 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, consulta la versión .NET 8 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.

Aplicaciones de muestra

Consulta las aplicaciones de ejemplo en el repositorio de GitHub de dotnet/blazor-samples.

BlazorWebAppCallWebApi

Llama a una API web de lista de tareas pendientes externa (no en la Blazor Web App) desde una Blazor Web App:

  • Backend: una aplicación de API web para mantener una lista de tareas pendientes, basada en 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 creados previamente e interactivos, los componentes de WebAssembly y los componentes automáticos que se representado previamente o han adoptado SSR, las llamadas se realizan con un HttpClient registrado en el archivo Program del proyecto de servidor (BlazorApp):

builder.Services.AddHttpClient();

Llama a una API de lista de películas interna (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 listas de películas es una API web.

En el ejemplo siguiente se obtiene una lista de películas del punto de conexión de /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();
}

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, basada en 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.

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 orientación que se aplica a las aplicaciones del lado del servidor, consulta Realizar peticiones HTTP utilizando IHttpClientFactory en ASP.NET Core.

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

En el archivo Program:

builder.Services.AddHttpClient();

El siguiente componente de Razor realiza una solicitud a una API web para ramas de GitHub similares al ejemplo Uso básico del artículo Realización de solicitudes HTTP con 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 Blazor Server 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()
    {
        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();

        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#, crea una matriz vacía (Array.Empty<GitHubBranch>()).

Para obtener un ejemplo operativo adicional, consulta el ejemplo de carga de archivos del lado servidor en el que se cargan archivos en un controlador de API web en el artículo Cargas de archivos de Blazor en ASP.NET Core.

Abstracciones de servicio para llamadas API web

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

Al usar los modos interactivos de representación de WebAssembly y Auto, los componentes se anteponen de forma predeterminada. 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, consulta 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 devueltos al cliente en las aplicaciones de ejemplo BlazorWebAppOidc (patrón no BFF) o BlazorWebAppOidcBff (patrón BFF). Estas aplicaciones muestran llamadas API seguras (web). Para obtener más información, consulta Protección de una Blazor Web App de ASP.NET Core con OpenID Connect (OIDC)..

API web externas de Blazor Web App

Esta sección se aplica a Blazor Web App que llaman a una API web mantenida por un proyecto independiente (externo), posiblemente hospedado en otro servidor.

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 una Blazor Web App. Si la aplicación se ejecuta solo con los servicios registrados HttpClient en el proyecto .Client, como se describe en la sección Agregar el servicioHttpClient, la ejecución de la aplicación produce un error en runtime:

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".

Usa cualquiera de los procedimientos siguientes:

  • Agrega 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 pendientes en la aplicación de ejemplo BlazorWebAppCallWebApi

  • Si la representación previa no es necesaria para un componente de WebAssembly que llama a la API web, deshabilita la representación previa siguiendo la guía de Modos de representación de Blazor de ASP.NET Core. 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, consulta 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.

Puedes abordar esta cuestión haciendo fluir el estado de representación previa usando la API de estado de componente persistente, que las aplicaciones de BlazorWebAppCallWebApi y BlazorWebAppCallWebApi_Weatherde ejemplo demuestran. 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:

Adición del servicio HttpClient

La guía de esta sección se aplica 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:

  • Denominada 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.
  • Tipada HttpClient: cada API web está tipada. Cuando el código de la aplicación o un componente Razor llama a una API web, usa una instancia tipada HttpClient 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 href de la etiqueta <base> 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 llama a una API web externa (que no se encuentra en el mismo espacio de direcciones URL que la aplicación cliente), establezca 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. HttpClient se implementa mediante Fetch API del explorador y está sujeto a sus limitaciones, incluido el cumplimiento de la directiva del mismo origen, que se trata 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. Inserte una instancia de HttpClient en un componente con la directiva @inject:

@using System.Net.Http
@inject HttpClient Http

Use 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 los asistentes 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 código de componente siguiente, un elemento enlazado del componente proporciona newItemName. 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, use 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. Consulta la sección GET from JSON (GetFromJsonAsync) para ver 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, use 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 la API web de JsonPatch en ASP.NET Core.

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

PatchAsJsonAsync devuelve un valor de HttpResponseMessage. Para deserializar el contenido JSON del mensaje de respuesta, use 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:

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

Diseñado con sangría, espaciado y comillas sin escape, 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.

Instale el paquete NuGet Microsoft.AspNetCore.JsonPatch y use 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, consulta los artículos de Instalación y administración de paquetes en 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 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 IsComplete true 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.

Agrega JsonSerializerOptions.WriteIndented establecido en true si quieres presentar la carga de JSON en un formato agradable para su visualización. La escritura de JSON con sangría no tiene ningún efecto en el procesamiento de solicitudes PATCH y, por lo general, no se realiza en aplicaciones de producción para solicitudes de API web.

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

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

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, agregue una directiva @using para el espacio de nombres Microsoft.AspNetCore.JsonPatch:

using Microsoft.AspNetCore.JsonPatch;

Proporcione 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 artículo JsonPatch en la API web de ASP.NET Core, la API de PATCH anterior no protege la API web de ataques de publicación excesiva. Para más información, consulte Tutorial: Creación de una nueva API web con ASP.NET Core.

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

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 servicios IHttpClientFactory y la configuración de una instancia de 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 más información, vea la sección HttpClient con tipo.

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

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulta los artículos de Instalación y administración de paquetes en 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 los componentes del lado cliente con nombre deben usarse en componentes del lado cliente creados previamente de una Blazor Web App, el registro de servicio anterior debe aparecer en el proyecto de servidor y 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 href de la etiqueta <base> 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 código de componente siguiente:

  • Una instancia de IHttpClientFactory crea un HttpClient con nombre.
  • El objeto HttpClient con nombre se usa para emitir una solicitud GET para los datos de previsión meteorológica JSON 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 aplicación de ejemplo BlazorWebAppCallWebApi muestra cómo llamar a una API web con un nombre HttpClient en su componente CallTodoWebApiCsrNamedClient. Para ver una demostración de trabajo adicional en una aplicación cliente basada en llamar a Microsoft Graph con un HttpClient denominado, consulte Usar Graph API con ASP.NET CoreBlazor WebAssembly.

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

Con tipo HttpClient

La instancia de HttpClient con tipo usa 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, consulta la secciónHttpClient con nombre con IHttpClientFactory.

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

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulta los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (documentación de NuGet). Confirma 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 con tipo debe usarse en componentes del lado cliente representados previamente de una Blazor Web App, el registro de servicio anterior debe aparecer en el proyecto de servidor y 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 href de la etiqueta <base> 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 código de componente siguiente:

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

...

@code {
    private Forecast[]? forecasts;

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

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

La guía de esta sección se aplica a los escenarios del lado cliente que dependen de 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);
    }
}

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

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

var requestMessage = new HttpRequestMessage() { ... };

requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
requestMessage.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 la API) y HttpRequestMessage se pueden usar para personalizar 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()
    {
        var requestMessage = 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))
        {
            requestMessage.Headers.Authorization =
                new AuthenticationHeaderValue("Bearer", token.Value);

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

            var response = await Http.SendAsync(requestMessage);
            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; }
    }
}

La implementación del lado cliente de Blazor de HttpClient usa la API Fetch y configura las opciones subyacentes de la API Fetch específicas de la solicitud a través de métodos de extensión HttpRequestMessage 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.

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 la transmisión de respuestas, usa el método de extensión SetBrowserResponseStreamingEnabled en la solicitud.

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

requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

Para obtener más información sobre las opciones de Fetch API, consulta la sección de parámetros de WindowOrWorkerGlobalScope.fetch() en MDN Web Docs.

Control 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, consulta Control de errores en aplicaciones Blazor de ASP.NET Core.

Uso compartido de recursos entre orígenes (CORS)

La seguridad del explorador restringe que una página web realice solicitudes a un dominio diferente del que ha proporcionado esa 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, consulta 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 del cliente, consulta Escenarios de seguridad adicionales de ASP.NETBlazor WebAssembly Core .

Compatibilidad con antiforgería

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();
    var request = new HttpRequestMessage(HttpMethod.Post, "action");
    request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
    var response = await client.SendAsync(request);
    ...
}

Para obtener más información, consulta Autenticación y autorización Blazor de ASP.NET Core.

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:

Los recursos HttpClientTest del repositorio de GitHub dotnet/aspnetcore

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 de una versión específica, usa la lista desplegable Cambiar ramas o etiquetas. Para obtener más información, consulta Procedimientos para seleccionar una etiqueta de versión de código fuente de ASP.NET 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 "publicación excesiva" significa literalmente que el usuario malintencionado publica en exceso en el formulario.

Para obtener instrucciones sobre la mitigación de ataques de publicación excesiva, consulta Tutorial: Creación de una API web con ASP.NET Core.

Lado servidor

Lado cliente