Поделиться через


Вызов веб-API из ASP.NET Core Blazor

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см. версию .NET 9 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В версии очередного релиза см. статью о .NET 9.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем релизе см. версию .NET 9 этой статьи.

В этой статье описывается вызов веб-API из приложения Blazor.

Пакет

Пакет System.Net.Http.Json предоставляет методы расширения для System.Net.Http.HttpClient и System.Net.Http.HttpContent, которые выполняют автоматическую сериализацию и десериализацию с помощью System.Text.Json. Пакет System.Net.Http.Json предоставляется общей платформой .NET и не требует добавления ссылки на пакет в приложение.

Примеры приложений

Ознакомьтесь с примерами dotnet/blazor-samples приложений в репозитории GitHub.

BlazorWebAppCallWebApi

Вызов внешнего веб-API списка дел, который не находится в Blazor Web App, из Blazor Web App:

  • Backend: веб API-приложение для ведения списка дел на основе минимальных API. Веб-приложение API — это отдельное приложение от Blazor Web App, которое, возможно, размещено на другом сервере.
  • BlazorApp / BlazorApp.Client: вызывает Blazor Web App веб-приложение API с HttpClient операциями списка todo, такими как создание, чтение, обновление и удаление элементов (CRUD) из списка тодо.

Для отрисовки на стороне клиента (CSR), которая включает в себя интерактивные компоненты WebAssembly и автокомпоненты, использующие CSR, вызовы выполняются с предварительно настроенным HttpClient зарегистрированным в Program файле клиентского проекта (BlazorApp.Client):

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

Для рендеринга на стороне сервера (SSR), который включает предварительно созданные и интерактивные серверные компоненты, предварительно созданные компоненты WebAssembly и автоматические компоненты, которые предварительно создаются или используют SSR, вызовы выполняются с HttpClient, зарегистрированным в Program файле серверного проекта (BlazorApp):

builder.Services.AddHttpClient();

Вызовите внутренний API списка фильмов (внутри Blazor Web App), который находится в серверном проекте Blazor Web App.

  • BlazorApp: Blazor Web App, который ведет список фильмов.
    • При выполнении операций в списке фильмов в приложении на сервере используются обычные вызовы API.
    • Когда вызовы API выполняются веб-клиентом, веб-API используется для операций списка фильмов на основе минимальных API.
  • BlazorApp.Client: клиентский проект Blazor Web App, содержащий интерактивные компоненты WebAssembly и Auto для управления списком фильмов пользователем.

Для CSR, включающего компоненты Interactive WebAssembly и авто-компоненты, принявшие CSR, вызовы API выполняются через клиентскую службу (ClientMovieService), которая использует предварительно настроенный HttpClient в файле Program клиентского проекта (BlazorApp.Client). Так как эти вызовы выполняются через общедоступный или частный веб-сайт, API списка фильмов — это веб-API.

В следующем примере показано, как получить список фильмов из конечной /movies точки:

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

Для SSR, включающего предварительно созданные и интерактивные компоненты сервера, предварительно созданные компоненты WebAssembly и автоматические компоненты, предварительно созданные или принятые SSR, вызовы выполняются непосредственно через серверную службу (ServerMovieService). API не зависит от сети, и поэтому представляет собой стандартный интерфейс для операций CRUD со списком фильмов.

В следующем примере показано, как получить список фильмов:

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

Пример приложения для работы с погодными данными, использующего потоковую обработку данных.

BlazorWebAssemblyCallWebApi

Вызывает веб-API списка дел из Blazor WebAssembly приложения:

  • Backend: веб-приложение для ведения списка дел на основе Minimal APIs.
  • BlazorTodo: Приложение Blazor WebAssembly, которое вызывает веб-API с предварительно настроенной системой HttpClient для операций CRUD со списком дел.

Сценарии на стороне клиента для вызова внешних веб-API

Клиентские компоненты вызывают внешние веб-API с помощью экземпляров HttpClient, которые обычно создаются с предварительно настроенным HttpClient, зарегистрированным в файле Program:

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

Следующий компонент Razor выполняет запрос к веб-API для получения ветвей GitHub, похожих на пример базового использования в статье Выполнения HTTP-запросов с помощью IHttpClientFactory в 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()
    {
        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 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; }
    }
}

В предыдущем примере для C# 12 или более поздней версии для переменной создается пустой [] массив (branches). Для более ранних версий C#, скомпилированных с помощью пакета SDK до .NET 8, создайте пустой массив (Array.Empty<GitHubBranch>()).

Чтобы защитить код и данные .NET/C#, используйте функции ASP.NET Core Data Protection с серверным веб-API ASP.NET Core. Клиентское Blazor WebAssembly приложение вызывает серверный веб-API для безопасных функций приложений и обработки данных.

Blazor WebAssembly приложения часто не могут осуществлять прямые вызовы между различными источниками веб-API из-за механизма безопасности CORS (совместного использования междоменных запросов). Обычное исключение выглядит следующим образом:

Доступ к запросу по адресу "{URL}" из источника "https://localhost:{PORT}'" был заблокирован политикой CORS: в запрошенном ресурсе отсутствует заголовок Access-Control-Allow-Origin. Если непрозрачный ответ служит вашим потребностям, задайте для режима запроса значение no-cors, чтобы получить ресурс с отключенным CORS.

Даже если вы вызываете SetBrowserRequestMode с полем BrowserRequestModeNoCors (1), которое пытается обойти предыдущее исключение, запрос часто завершается ошибкой из-за ограничений CORS в источнике веб-API, например ограничение, которое разрешает вызовы только из определенных источников или ограничение, которое предотвращает запросы JavaScript fetch из браузера. Единственный способ, чтобы такие вызовы были успешными, заключается в том, что веб-API, которое вы вызываете, должно разрешить вашему источнику вызывать его источник с правильной конфигурацией CORS. Большинство внешних веб-API не позволяют настраивать политики CORS. Чтобы справиться с этим ограничением, выполните любую из следующих стратегий:

  • Поддерживайте собственное серверное веб-API ASP.NET Core. Клиентское Blazor WebAssembly приложение вызывает веб-API на стороне сервера, а веб-API выполняет запрос из кода C# на основе сервера (а не браузера) во внешний веб-API с правильными заголовками CORS, возвращая результат в клиентское Blazor WebAssembly приложение.

  • Используйте службу прокси-сервера для прокси-запроса из клиентского приложения Blazor WebAssembly в внешний веб-API. Прокси-служба использует серверное приложение для выполнения запроса от имени клиента и возвращает результат после успешного вызова. В следующем примере, базирующемся на CORS PROXY от CloudFlare, заполнитель {REQUEST URI} является URI запроса:

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

Сценарии на стороне сервера для вызова внешних веб-API

Серверные компоненты вызывают внешние веб-API с помощью экземпляров, обычно созданных с помощью HttpClientIHttpClientFactory. Инструкции, применимые к приложениям на стороне сервера, см. в статье "Создание HTTP-запросов с помощью IHttpClientFactory" в ASP.NET Core.

Серверное приложение не включает HttpClient службу. Предоставьте HttpClient для приложения с помощью фабричной инфраструктуры HttpClient.

В файле Program:

builder.Services.AddHttpClient();

Следующий компонент Razor выполняет запрос к веб-API для ветвей GitHub, аналогично Примеру базового использования в статье Выполнение HTTP-запросов с помощью IHttpClientFactory в 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()
    {
        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; }
    }
}

В предыдущем примере для C# 12 или более поздней версии для переменной создается пустой [] массив (branches). Для более ранних версий C#, скомпилированных с помощью пакета SDK до .NET 8, создайте пустой массив (Array.Empty<GitHubBranch>()).

Для дополнительного рабочего примера см. пример отправки файлов на сервере, который загружает файлы в контроллер веб-API в статье BlazorASP.NET Core "Отправка файлов".

Абстракции служб для вызовов веб-API

Этот раздел применяется к Blazor Web Apps, которые обеспечивают поддержку веб-API в серверном проекте или преобразуют запросы веб-API к внешнему API.

При использовании интерактивных режимов webAssembly и автоматического отрисовки компоненты по умолчанию создаются предварительно. Автокомпоненты также изначально интерактивно отображаются на сервере перед загрузкой Blazor пакета клиенту и активацией клиентской среды выполнения. Это означает, что компоненты, использующие эти режимы отрисовки, должны быть разработаны таким образом, чтобы они успешно выполнялись как от клиента, так и от сервера. Если компонент должен вызвать API на основе проекта сервера или преобразовать запрос во внешний веб-API (который находится за пределами Blazor Web Appклиента), рекомендуется абстрагировать этот вызов API за интерфейсом службы и реализовать версии клиента и сервера службы:

  • Версия клиента вызывает веб-API с предварительно настроенной HttpClientконфигурацией.
  • Версия сервера обычно может напрямую получить доступ к ресурсам на стороне сервера. Внедрение HttpClient на сервере, который выполняет обратные вызовы к серверу, не рекомендуется, так как сетевой запрос обычно является ненужным. Кроме того, API может быть внешним для проекта сервера, но абстракция службы для сервера требуется для преобразования запроса каким-то образом, например для добавления маркера доступа к прокси-запросу.

При использовании режима WebAssembly вы также можете отключить предварительную отрисовку, чтобы компоненты рендерились только на клиенте. Для получения дополнительной информации см. режимы рендеринга в ASP.NET CoreBlazor.

Примеры (примеры приложений):

  • Веб-API списка фильмов в BlazorWebAppCallWebApi примере приложения.
  • Веб-API для стримингового рендеринга погодных данных в примере приложения BlazorWebAppCallWebApi_Weather.
  • Данные о погоде возвращаются клиенту в примерах приложений, которые используют либо BlazorWebAppOidc (ненаборный шаблон), либо BlazorWebAppOidcBff (BFF-шаблон). Эти приложения демонстрируют безопасные (веб-) вызовы API. Дополнительные сведения см. в статье "Защита ASP.NET Core Blazor Web App с помощью OpenID Connect (OIDC)".

Blazor Web App внешние веб-API

Этот раздел относится к Blazor Web Appам, которые вызывают веб-API, поддерживаемый отдельным (внешним) проектом, возможно размещённым на другом сервере.

Blazor Web Appобычно предварительно отрисованные клиентские компоненты WebAssembly, и компоненты Auto рендерятся на сервере во время статической или интерактивной отрисовки на стороне сервера (SSR). HttpClient Службы по умолчанию не регистрируются в Blazor Web App основном проекте. Если приложение выполняется только с HttpClient услугами, зарегистрированными в .Client проекте, как описано в разделе Добавление службы HttpClient, выполнение приложения приводит к ошибке во время выполнения:

InvalidOperationException: не удается указать значение свойства "Http" в типе "... {COMPONENT}'. Зарегистрированная служба типа System.Net.Http.HttpClient отсутствует.

Используйте любой из следующих подходов:

  • Добавьте службы в проект сервера, чтобы сделать их доступными во время SSR. Используйте следующую регистрацию службы в файле проекта Program сервера:

    builder.Services.AddHttpClient();
    

    HttpClient службы предоставляются общей платформой, поэтому ссылка на пакет в файле проекта приложения не требуется.

    Пример. Веб-API списка todo в BlazorWebAppCallWebApiпримере приложения

  • Если пререндеринг не требуется для компонента WebAssembly, который вызывает веб-API, отключите пререндеринг, следуя инструкциям в ASP.NET Core по режимам рендеринга. Если вы используете этот подход, вам не нужно добавлять HttpClient службы в основной проект Blazor Web App , так как компонент не предопределен на сервере.

Для получения дополнительной информации см. в разделе «Не удается разрешить клиентские службы перед рендерингом».

Предварительно обработанные данные

При предварительной подготовке компоненты дважды отображаются: сначала статически, а затем интерактивно. Состояние не передается автоматически от предварительно отрисованного компонента к интерактивному. Если компонент выполняет асинхронные операции инициализации и отрисовывает разное содержимое для различных состояний во время инициализации, например, индикатор прогресса "Загрузка...", вы можете заметить мерцание, когда компонент отрисовывается дважды.

Для этого можно управлять предварительно созданным состоянием с помощью API состояния сохраняемого компонента, что демонстрируется в примерах приложений . Когда компонент рендерится в интерактивном режиме, он может отображаться так же, используя то же состояние. Однако API в настоящее время не работает с расширенной навигацией, которую можно обойти, отключив расширенную навигацию по ссылкам на страницу (data-enhanced-nav=false). Дополнительные сведения см. на следующих ресурсах:

Потоковая передача запросов на стороне клиента

Для браузеров на основе Chromium (например, Google Chrome и Microsoft Edge), использующих протокол HTTP/2 и HTTPS, на стороне клиента Blazor используется API Streams для разрешения потоковой передачи запросов.

Чтобы включить потоковую передачу запросов, задайте для SetBrowserRequestStreamingEnabled значение true на HttpRequestMessage.

В следующем примере отправки файла:

  • content — это HttpContentфайла.
  • /Filesave — это конечная точка веб-API.
  • Http — это HttpClient.
var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
request.SetBrowserRequestStreamingEnabled(true);
request.Content = content;

var response = await Http.SendAsync(request);

Потоковые запросы:

  • Требовать использования протокола HTTPS и не поддерживать работу с HTTP/1.x.
  • Включите текст, но не заголовок Content-Length. CORS с предварительным запросом требуется для запросов потоковой передачи между источниками.

Дополнительные сведения о загрузке файлов с компонентом InputFile см. в разделе ASP.NET Core Blazor загрузка файлов и примере загрузки файлов на сервер с использованием клиентской отрисовки (CSR).

Добавьте службу HttpClient

Рекомендации в этом разделе относятся к сценариям на стороне клиента.

Клиентские компоненты вызывают веб-API с помощью предварительно настроенной HttpClient службы, которая сосредоточена на выполнении запросов обратно на сервер источника. Дополнительные конфигурации службы HttpClient для других веб-API можно создать в коде разработчика. Запросы формируются с помощью вспомогательных приложений JSON Blazor или с помощью HttpRequestMessage. Запросы могут включать в себя конфигурацию параметра Fetch API.

Примеры конфигурации в этом разделе полезны только при вызове одного веб-API для одного HttpClient экземпляра в приложении. Когда приложение должно вызывать несколько веб-API, каждый из которых имеет собственный базовый адрес и конфигурацию, можно применить следующие подходы, описанные далее в этой статье:

  • Названо HttpClient с IHttpClientFactory: каждому веб-API присваивается уникальное имя. Если код приложения или Razor компонент вызывает веб-API, он использует именованный HttpClient экземпляр для вызова.
  • Типизированный HttpClient: каждый веб-API типизирован. Если код приложения или Razor компонент вызывает веб-API, он использует типизированный HttpClient экземпляр для вызова.

В файле Program добавьте службу HttpClient, если она еще не добавлена из шаблона проекта Blazor, использованного для создания приложения.

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

В предыдущем примере задается базовый адрес (builder.HostEnvironment.BaseAddress), который получает базовый адрес IWebAssemblyHostEnvironment.BaseAddress для приложения и обычно является производным от <base> значения тега href на хост-странице.

Наиболее распространенными случаями использования собственного базового адреса клиента являются:

  • Клиентский проект (.Client) на платформе Blazor Web App (.NET 8 или более поздней версии) выполняет вызовы веб-API из компонентов или кода WebAssembly, которые выполняются на клиенте в WebAssembly, к API в серверном приложении.
  • Клиентский проект (Client) приложения, размещённого на сервере Blazor WebAssembly, выполняет веб-API запросы к серверному проекту (Server). Обратите внимание, что шаблон проекта Hosted Blazor WebAssembly больше недоступен в .NET 8 или в более поздних версиях. Однако размещенные Blazor WebAssembly приложения остаются поддерживаемыми для .NET 8.

Если вы вызываете внешний веб-API (не в том же пространстве URL-адресов, что и клиентское приложение), установите URI на базовый адрес веб-API. Следующий пример задает базовый адрес веб-API, в котором выполняется отдельное веб-приложение API https://localhost:5001и готово к ответу на запросы из клиентского приложения:

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

Вспомогательные инструменты JSON

HttpClient доступен в качестве предварительно настроенной службы для отправки запросов обратно к серверу-источнику.

HttpClient и вспомогательные функции JSON (System.Net.Http.Json.HttpClientJsonExtensions) также используются для вызова сторонних конечных точек API. HttpClient реализуется с помощью Fetch API браузера и подлежит его ограничениям, включая применение политики единого источника, которая обсуждается далее в этой статье в разделе о совместном использовании ресурсов (CORS).

Базовый адрес клиента устанавливается как адрес сервера-источника. Внедрите экземпляр HttpClient в компонент с помощью директивы @inject:

@using System.Net.Http
@inject HttpClient Http

Используйте пространство имен System.Net.Http.Json для доступа к HttpClientJsonExtensions, включая GetFromJsonAsync, PutAsJsonAsync и PostAsJsonAsync:

@using System.Net.Http.Json

В следующих разделах рассматриваются вспомогательные средства JSON:

System.Net.Http включает дополнительные методы для отправки HTTP-запросов и получения HTTP-ответов, например для отправки запроса DELETE. Дополнительные сведения см. в разделе DELETE и дополнительные методы расширения.

Получение данных с помощью GET из JSON (GetFromJsonAsync)

GetFromJsonAsync отправляет HTTP-запрос GET и анализирует текст ответа JSON для создания объекта.

В следующем коде компонента todoItems отображаются компонентом. GetFromJsonAsync вызывается после завершения инициализации компонента (OnInitializedAsync).

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

Запрос POST в формате JSON (PostAsJsonAsync)

PostAsJsonAsync отправляет запрос POST по указанному URI, содержащий сериализованное значение в формате JSON в теле запроса.

В следующем коде компонента newItemName предоставляется связанным элементом компонента. Метод AddItem активируется путем выбора элемента <button>.

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

PostAsJsonAsync возвращает HttpResponseMessage. Чтобы десериализовать содержимое JSON из ответного сообщения, используйте метод расширения ReadFromJsonAsync. В следующем примере данные погоды JSON считываются в виде массива:

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

Запрос PUT в формате JSON (PutAsJsonAsync)

PutAsJsonAsync отправляет HTTP-запрос PUT, включая содержимое в кодировке JSON.

В следующем коде компонента значения editItem для Name и IsCompleted предоставляются связанными элементами компонента. Элемент Id задается, когда элемент выбирается в другой части пользовательского интерфейса (не показан) и вызывается EditItem. Метод SaveItem активируется путем выбора элемента <button>. В следующем примере не отображается загрузка todoItems для краткости. Пример загрузки элементов см. в разделе GET из JSON (GetFromJsonAsync).

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

PutAsJsonAsync возвращает HttpResponseMessage. Чтобы десериализовать содержимое JSON из ответного сообщения, используйте метод расширения ReadFromJsonAsync. В следующем примере данные погоды JSON считываются в виде массива:

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

PATCH как JSON (PatchAsJsonAsync)

PatchAsJsonAsync отправляет HTTP-запрос PATCH с содержимым в кодировке JSON.

Примечание.

Дополнительные сведения см. в разделе JsonPatch в веб-API ASP.NET Core.

В следующем примере PatchAsJsonAsync получает документ JSON PATCH в виде строки обычного текста с экранируемыми кавычками:

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

По состоянию на C# 11 (.NET 7) можно создать строку JSON в виде необработанного строкового литерала. Укажите синтаксис JSON с помощью поля StringSyntaxAttribute.Json для атрибута и[StringSyntax] для средств анализа кода.

@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 возвращает HttpResponseMessage. Чтобы десериализовать содержимое JSON из ответного сообщения, используйте метод расширения ReadFromJsonAsync. В следующем примере данные элемента JSON задачи считываются в виде массива. Пустой массив создается, если данные элемента не возвращаются методом, поэтому content не имеет значения NULL после выполнения инструкции:

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

Представленный с отступами, интервалами и незащищенными кавычками, не кодированный PATCH документ отображается как следующий JSON:

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

Чтобы упростить создание документов PATCH в приложении, выдавающем запросы PATCH, приложение может использовать поддержку .NET JSON PATCH, как показано в следующем руководстве.

Microsoft.AspNetCore.JsonPatch Установите пакет NuGet и используйте функции API пакета для создания JsonPatchDocument запроса PATCH.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Добавьте директивы @using для System.Text.Json, System.Text.Json.Serialization и Microsoft.AspNetCore.JsonPatch пространств имен в верхнюю часть компонента Razor.

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

Создайте JsonPatchDocument для TodoItem с IsComplete, установленного для true, с помощью метода Replace.

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

Передайте операции документа (patchDocument.Operations) вызову PatchAsJsonAsync :

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

JsonSerializerOptions.DefaultIgnoreCondition устанавливается в JsonIgnoreCondition.WhenWritingDefault, чтобы игнорировать свойство, только если оно равно значению по умолчанию для его типа.

Добавьте JsonSerializerOptions.WriteIndented к true, если хотите представить полезные данные JSON в удобочитаемом формате для отображения. Использование отступов в JSON не влияет на обработку запросов PATCH и обычно не выполняется в производственных приложениях для запросов веб-API.

Следуйте указаниям в статье JsonPatch в ASP.NET Core web API, чтобы добавить действие контроллера PATCH в веб-API. Кроме того, обработка запросов PATCH может быть реализована как минимальный API с помощью следующих шагов.

Добавьте ссылку на пакет Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet в веб-API приложение.

Примечание.

Нет необходимости добавлять в приложение ссылку на пакет Microsoft.AspNetCore.JsonPatch, так как ссылка на пакет Microsoft.AspNetCore.Mvc.NewtonsoftJson автоматически и транзитивно добавляет ссылку на пакет Microsoft.AspNetCore.JsonPatch.

Добавьте в файл Program директиву @using для пространства имен Microsoft.AspNetCore.JsonPatch.

using Microsoft.AspNetCore.JsonPatch;

Предоставьте конечную точку конвейеру обработки запросов веб-API:

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

Предупреждение

Как и в других примерах в статье JsonPatch в ASP.NET Core web API, предыдущий API PATCH не защищает веб-API от атак избыточной передачи данных. Дополнительные сведения см. в руководстве по . Создание веб-API на основе контроллера с помощью ASP.NET Core.

Полный рабочий интерфейс PATCH см. в BlazorWebAppCallWebApiпримере приложения.

DELETE (DeleteAsync) и дополнительные методы расширения

System.Net.Http включает дополнительные методы расширения для отправки HTTP-запросов и получения HTTP-ответов. HttpClient.DeleteAsync используется для отправки запроса HTTP DELETE в веб-интерфейс API.

В следующем коде компонента элемент <button> вызывает метод DeleteItem. Связанный элемент <input> предоставляет id удаляемого элемента.

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

Названный HttpClient с IHttpClientFactory

Поддерживаются службы IHttpClientFactory и конфигурация именованного класса HttpClient.

Примечание.

Вместо именованного класса HttpClient из IHttpClientFactory можно использовать типизированный класс HttpClient. Дополнительные сведения см. в разделе Типы HttpClient.

Добавьте в приложение пакет NuGet Microsoft.Extensions.Http.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

В файле Program клиентского проекта:

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

Если именованный клиент будет использоваться предварительно отрисованными клиентскими компонентами объекта Blazor Web App, предыдущая регистрация службы должна отображаться как в серверном проекте, так и в проекте .Client. На сервере builder.HostEnvironment.BaseAddress заменяется базовым адресом веб-API, описанным ниже.

Предыдущий пример на стороне клиента задает базовый адрес, используя builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), который получает базовый адрес для клиентского приложения и обычно извлекается из значения тега <base> на хост-странице.

Наиболее распространенными вариантами использования собственного базового адреса клиента являются следующие:

  • Клиентский проект (.Client) типа Blazor Web App, который совершает вызовы API через компоненты или код WebAssembly/Auto, выполняющийся на клиенте в среде WebAssembly, к API в серверном приложении по тому же адресу хоста.
  • Клиентский проект (Client) размещенного Blazor WebAssembly приложения, которое осуществляет вызовы веб-API к серверному проекту (Server).

Наиболее распространенный случай использования собственного базового адреса клиента — это клиентский проект (Client) приложения Blazor WebAssembly, размещенного на сервере, который выполняет вызовы веб-API к серверному проекту (Server).

Если вы вызываете внешний веб-API (не в том же пространстве URL-адресов, что и клиентское приложение) или настраиваете службы в серверном приложении (например, для предварительного создания компонентов на сервере), задайте URI для базового адреса веб-API. Следующий пример задает базовый адрес веб-API, в котором выполняется отдельное веб-приложение API https://localhost:5001и готово к ответу на запросы из клиентского приложения:

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

В следующем коде компонента:

  • Экземпляр IHttpClientFactory создает именованный объект HttpClient.
  • Имя HttpClient используется для отправки запроса GET на получение данных прогноза погоды JSON из веб-API по адресу /forecast.
@inject IHttpClientFactory ClientFactory

...

@code {
    private Forecast[]? forecasts;

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

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

Пример BlazorWebAppCallWebApiприложения демонстрирует вызов веб-API с именем HttpClient в его CallTodoWebApiCsrNamedClient компоненте. Дополнительные рабочие демонстрации в клиентском приложении на основе вызова Microsoft Graph с именем HttpClientсм. в разделе "Использование API Graph с ASP.NET Core Blazor WebAssembly".

Для рабочей демонстрации в клиентском приложении, основанном на вызове Microsoft Graph с именем HttpClient, см. статью "Использование API Graph с ASP.NET Core Blazor WebAssembly".

Введено HttpClient

Для возврата данных из одной или нескольких конечных точек веб-API типизированный класс HttpClient использует один или несколько экземпляров класса HttpClient приложения (заданных по умолчанию или именованных).

Примечание.

Вместо типизированного HttpClient можно использовать именованный HttpClient из IHttpClientFactory. Дополнительные сведения см. в разделе Называется HttpClient с IHttpClientFactory.

Добавьте в приложение пакет NuGet Microsoft.Extensions.Http.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

В следующем примере возникает запрос GET для данных прогноза погоды JSON из веб-API /forecast.

ForecastHttpClient.cs:

using System.Net.Http.Json;

namespace BlazorSample.Client;

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

Program В файле клиентского проекта:

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

Если типизированный клиент должен использоваться предварительно отрисованными клиентскими компонентами объекта Blazor Web App, то предыдущая регистрация службы должна присутствовать как в серверном проекте, так и в проекте .Client. На сервере builder.HostEnvironment.BaseAddress заменяется базовым адресом веб-API, описанным ниже.

В предыдущем примере задается базовый адрес с помощью builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress), который используется для получения базового адреса клиентского приложения и обычно извлекается из значения тега <base>href на хост-странице.

Наиболее распространенные случаи использования собственного базового адреса клиента:

  • Клиентский проект (.Client) Blazor Web App, который делает вызовы веб-API из компонентов WebAssembly/Auto или кода, работающего на клиентской стороне с помощью WebAssembly, к API серверного приложения по тому же адресу хоста.
  • Клиентский проект (Client) размещенного Blazor WebAssembly приложения, выполняющего веб-API вызовы к серверному проекту (Server).

Наиболее распространенным случаем использования собственного базового адреса клиента является клиентский проект (Client) размещенного Blazor WebAssembly приложения, который выполняет вызовы веб-API к проекту сервера (Server).

Если вы вызываете внешний веб-API (не в том же пространстве URL-адресов, что и клиентское приложение) или настраиваете службы в серверном приложении (например, для предварительного создания компонентов на сервере), задайте URI для базового адреса веб-API. Следующий пример задает базовый адрес веб-API, в котором выполняется отдельное веб-приложение API https://localhost:5001и готово к ответу на запросы из клиентского приложения:

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

Компоненты внедряют типизированное HttpClient для вызова веб-API.

В следующем коде компонента:

  • Внедряется экземпляр предыдущего класса ForecastHttpClient, который создает типизированный объект HttpClient.
  • Типизированный объект HttpClient используется для выдачи запроса GET для получения из веб-API данных прогноза погоды в формате JSON.
@inject ForecastHttpClient Http

...

@code {
    private Forecast[]? forecasts;

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

Примерное BlazorWebAppCallWebApiприложение демонстрирует вызов веб-API с типизированным HttpClient в его CallTodoWebApiCsrTypedClient компоненте. Обратите внимание, что компонент принимает отрисовку на стороне клиента (CSR) (InteractiveWebAssembly режим отрисовки) с предварительным рендерингом, поэтому типизированная регистрация клиентской службы появляется в файле как серверного проекта, так и проекта .Client.

Руководство в этом разделе относится к сценариям на стороне клиента, которые используют проверку подлинности cookie.

Для аутентификации на основе cookie, которая считается более безопасной, чем аутентификация на основе токенов, учетные данные cookie можно отправлять с каждым запросом к веб-API, вызывая AddHttpMessageHandler с помощью DelegatingHandler на предварительно настроенном HttpClient. Обработчик настраивает SetBrowserRequestCredentials с помощью BrowserRequestCredentials.Include, который инструктирует браузер отправлять учетные данные с каждым запросом, например, заголовки куки или заголовки HTTP-аутентификации, включая кросс-доменные запросы.

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

В Program файле регистрируется CookieHandler.

builder.Services.AddTransient<CookieHandler>();

Обработчик сообщений добавляется к любой предварительно настроенной HttpClient конфигурации, требующей cookie проверки подлинности:

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

Для демонстрации см. раздел Secure ASP.NET Core Blazor WebAssembly с ASP.NET Core Identity.

При создании HttpRequestMessageобъекта задайте учетные данные запроса браузера и заголовок напрямую:

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

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

HttpClient и HttpRequestMessage с параметрами запроса API Fetch

Руководство в этом разделе относится к сценариям на стороне клиента, которые используют проверку подлинности маркера носителя.

HttpClient (документация по API) и HttpRequestMessage можно использовать для настройки запросов. Например, можно указать метод HTTP и заголовки запроса. Следующий компонент выполняет запрос POST к конечной точке веб-API и отображает текст ответа.

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

Blazor клиентская реализация HttpClient использует Fetch API и настраивает основные опции Fetch API для конкретного запроса с помощью методов расширения HttpRequestMessage и WebAssemblyHttpRequestMessageExtensions. Задайте дополнительные параметры с помощью более универсального метода расширения SetBrowserRequestOption. Blazor и базовый Fetch API напрямую не добавляют и не изменяют заголовки запросов. Дополнительные сведения о том, как агенты пользователей, такие как браузеры, взаимодействуют с заголовками, см. в документации внешнего агента пользователя и других веб-ресурсах.

HTTP-ответ обычно помещается в буфер для выполнения операций синхронизации содержимого ответа. Чтобы включить поддержку потоковой передачи ответов, используйте метод расширения SetBrowserResponseStreamingEnabled для запроса.

Чтобы включить учетные данные в запрос независимо от источника, используйте метод расширения SetBrowserRequestCredentials.

requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

Дополнительные сведения о возможностях Fetch API см. на странице Веб-документация MDN. Параметры WindowOrWorkerGlobalScope.fetch().

Обработка ошибок

Обрабатывайте ошибки ответа веб-API в коде разработчика при их возникновении. Например, GetFromJsonAsync ожидает ответа JSON от веб-API с заголовком Content-Type типа application/json. Если формат ответа отличается от JSON, при проверке содержимого возникает исключение NotSupportedException.

В следующем примере показана неправильно написанная конечная точка URI для запроса данных прогноза погоды. URI должен иметь вид WeatherForecast, но отображается в вызове как WeatherForcast, в котором отсутствует буква e в Forecast.

Вызов GetFromJsonAsync ожидает возврата JSON, но веб-API возвращает HTML для необработанного исключения с заголовком Content-Type типа text/html. Необработанное исключение возникает из-за того, что путь к /WeatherForcast не найден, а ПО промежуточного слоя не может обслуживать страницу или представление для запроса.

Если выясняется, что содержимое ответа не является содержимым формата JSON, в методе OnInitializedAsync на клиенте возникает исключение NotSupportedException. Исключение перехватывается в блоке catch, где пользовательская логика может зафиксировать ошибку или вывести дружелюбное сообщение об ошибке для пользователя.

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

Примечание.

Предыдущий пример приведен только в качестве демонстрации. Веб-API можно настроить для возврата JSON, даже если конечная точка не существует или на сервере возникает необработанное исключение.

Дополнительные сведения см. в статье Обработка ошибок в приложениях Blazor ASP.NET Core.

Совместное использование ресурсов между источниками (CORS)

Безопасность браузера часто ограничивает веб-страницу от выполнения запросов к другому источнику, чем тот, который обслуживал веб-страницу. Это ограничение называется политика одного источника. Эта политика запрещает (но не предотвращает) чтение вредоносным сайтом конфиденциальных данных с другого сайта. Чтобы отправлять запросы из браузера в конечную точку с другим источником, конечная точка должна включить общий доступ к ресурсам между источниками (CORS).

Дополнительные сведения о CORS на стороне сервера см. в разделе "Включение запросов между источниками" в ASP.NET Core. Примеры статьи не относятся непосредственно к Razor сценариям компонентов, но статья полезна для обучения общим понятиям CORS.

Для получения информации о запросах CORS на стороне клиента см. раздел ASP.NET Core дополнительные сценарии безопасностиBlazor WebAssembly.

Поддержка антифальсификации

Чтобы добавить поддержку антифоргерии в HTTP-запрос, внедрите AntiforgeryStateProvider и добавьте RequestToken в коллекцию заголовков в качестве 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);
    ...
}

Дополнительные сведения см. в разделе ASP.NET Core Blazor аутентификация и авторизация.

Примеры компонентов платформы Blazor для тестирования доступа к веб-API

Различные сетевые средства доступны для тестирования внутренних приложений веб-API напрямую, например Firefox Browser Developer. Источник справочной информации платформы Blazor содержит HttpClient тестовые средства, полезные для тестирования.

Ресурсы HttpClientTest в репозитории GitHub dotnet/aspnetcore

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте выпадающий список «Переключение ветвей или тегов». Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Дополнительные ресурсы

Общие

Смягчение атак чрезмерной отправкой данных

Веб-API могут быть уязвимы к атаке избыточной отправки, также известной как атака массового назначения данных. Атака переопубликования происходит, когда злоумышленник отправляет HTML-форму POST на сервер, который обрабатывает данные для свойств, не входящих в отображаемую форму, и которые разработчик не хочет позволять пользователям изменять. Термин "overposting" буквально означает, что вредоносный пользователь чрезмерно использовал метод POST с помощью формы.

Рекомендации по устранению атак на переполнение смотрите в руководстве по созданию контроллерного веб-API с использованием ASP.NET Core.

на стороне сервера

На стороне клиента