Для взаимодействия с сервером компоненты Blazor WebAssembly, как и компоненты уровня сервера, также используют класс HttpClient. Однако поскольку компоненты WebAssembly отрабатывают на клиенте, то в реальности для отправки запросов они будут использовать Fetch API браузера. Соответственно принцип установки и получения HttpClient в компонентах WebAssembly будет немного различаться.
Пусть у нас есть следующие проекты
То есть у нас два проекта: главный проект BlazorApp и проект с компонентами WebAssembly - BlazorApp.Client. Поскольку на момент .NET 8 для хранения компонентов Blazor WebAssembly необходимо создавать дополнительный проект.
В главном проекте BlazorApp (на сервере) в файле Program.cs прописан следующий код:
using BlazorApp.Components; using BlazorApp.Client; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents() .AddInteractiveWebAssemblyComponents(); // поддержка компонентов WebAssembly builder.Services.AddHttpClient(); // регистрируем сервис файбрики HttpClient var app = builder.Build(); app.UseAntiforgery(); app.MapGet("/time", () => DateTime.Now.ToShortTimeString()); app.MapRazorComponents<App>() .AddInteractiveWebAssemblyRenderMode() // рендеринг компонентов WebAssembly .AddAdditionalAssemblies(typeof(Home).Assembly); ; // подключаем поиск компонентов из BlazorApp.Client app.Run();
Здесь добавлены сервисы компонентов WebAssembly, определена поддержка рендеринга на стороне клиента и добавлен поиск компонентов в сборке, где находится компонент Home из второго проекта.
Для использования HttpClient регистрируем сервис фабрики:
builder.Services.AddHttpClient();
Для теста здесь также определена конечная точка, которая при обращении по пути "/time" возвращает текущее время.
app.MapGet("/time", () => DateTime.Now.ToShortTimeString());
В главном проекте BlazorApp в папке "Components" есть один компонент - корневой компонент App, который загружает компонент Home из второго проекта:
@page "/" @using BlazorApp.Client <!DOCTYPE html> <html> <head> <title>METANIT.COM</title> <meta charset="utf-8" /> </head> <body> <Home /> <script src="_framework/blazor.web.js"></script> </body> </html>
И для работы с HttpClient нам надо добавить данный объект в качестве сервиса в приложение. Для этого во втором проекте - BlazorApp.Client определим следующий файл Program.cs:
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; var builder = WebAssemblyHostBuilder.CreateDefault(args); // добавляем HttpClient builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://localhost:7066/") }); await builder.Build().RunAsync();
При добавлении HttpClient устанавливается свойство BaseAddress, которое определяет адрес веб-сервиса. В моем случае веб-приложение ASP.NET Core запущено по адресу "https://localhost:7066/".
После этого мы можем получить объект HttpClient в коде компонентов, как и любой другой сервис, с помощью внедрения зависимостей. Так, определим в проекте BlazorApp.Client следующий компонент Home:
@using Microsoft.AspNetCore.Components.Web @using System.Net.Http.Json @rendermode RenderMode.InteractiveWebAssembly @inject HttpClient httpClient <h2>Time: @time</h2> <button @onclick="Submit">Click</button> @code { string? time; async Task Submit() { var response = await httpClient.GetAsync("/time"); time = await response.Content.ReadAsStringAsync(); } }
Здесь по нажатию на кнопку отправляем Get-запрос к веб-сервису по пути "/time" и получаем ответ в переменную response:
var response = await httpClient.GetAsync("/time");
Далее считываем содержимое ответа в строку в переменную time, значение которой выводится в заголовке на веб-страницу:
time = await response.Content.ReadAsStringAsync();
В остальном работа с HttpClient будет идти также как и в принципе в .NET.
Отправка запросов и получение ответов с помощью HttpClient в Blazor WebAssembly будет идти также, как и в целом в .NET - Отправка запросов с помощью HttpClient. Поэтому рассмотрим лишь один сценарий - отправку данных в POST-запросе. Пусть файл Program.cs в главном проекте BlazorApp выглядит следующим образом:
using BlazorApp.Components; using BlazorApp.Client; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents() ..AddInteractiveWebAssemblyComponents(); builder.Services.AddHttpClient(); // регистрируем сервис файбрики HttpClient var app = builder.Build(); app.UseAntiforgery(); app.MapPost("/user", (Person user) => { // если длина имени меньше 3 или больше 20 символов if (user.Name.Length < 3 || user.Name.Length > 20) return Results.BadRequest(new { details = "Имя должно иметь не меньше 3 и не больше 20 символов" }); // если возраст меньше 1 или больше 110 if (user.Age < 1 || user.Age > 110) return Results.BadRequest(new { details = "Некорректный возраст" }); // если все нормально, устанавливаем id для нового пользователя user.Id = Guid.NewGuid().ToString(); // посылаем объект в виде json return Results.Json(user); }); app.MapRazorComponents<App>() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(Home).Assembly); app.Run(); public class Person { public string Id { get; set; } = ""; public string Name { get; set; } = ""; public int Age { get; set; } }
Здесь предполагается, что клиент в POST-запросе по пути "/user" будет отправлять объект Person. При получении данных валидируем их (имя должно иметь от 3 до 20 символов, а возраст должен быть в диапазоне от 1 до 110). Если они не соответствуют некоторым ограничениям, то посылаем ошибку 400 и объект json с сообщением об ошибке:
if(user.Name.Length < 3 || user.Name.Length > 20) return Results.BadRequest(new {details="Имя должно иметь не меньше 3 и не больше 20 символов" }); if (user.Age < 1 || user.Age > 110) return Results.BadRequest(new {details = "Некорректный возраст" });
Если данные корректны, то устанавливаем у объекта Person свойство Id и посылаем его в формате JSON обратно клиенту:
user.Id = Guid.NewGuid().ToString(); return Results.Json(user);
Для взаимодействия с этим веб-приложением в проекте BlazorApp.Client определим следующий компонент Home:
@using Microsoft.AspNetCore.Components.Web @using System.Net.Http.Json @rendermode RenderMode.InteractiveWebAssembly @inject HttpClient httpClient <div style="color:red;">@message</div> <div> <p> Имя:<br /> <input @bind-value="person.Name" /> </p> <p> Возраст:<br /> <input type="number" @bind-value="person.Age" /> </p> <button @onclick="Submit">Click</button> </div> @code { string? message; Person person = new(); async Task Submit() { message = ""; var response = await httpClient.PostAsJsonAsync("https://localhost:7066/user", person); if (response.IsSuccessStatusCode) { var newPerson = await response.Content.ReadFromJsonAsync<Person>(); if (newPerson != null) message = $"Создан объект Person с id = {newPerson.Id}"; } else { var error = await response.Content.ReadFromJsonAsync<Error>(); if (error != null) message = error.Details; } } class Error { public string Details { get; set; } = ""; } class Person { public string Id { get; set; } = ""; public string Name { get; set; } = ""; public int Age { get; set; } } }
Здесь определена форма с двумя полями ввода, которые привязаны к свойствам Name и Age объекта Person. По нажатию на кнопку срабатывает метод Submit, который в POST-запросе в виде кода json отправляет на сервер объект Person.
При успешном запросе (если сервер возвращает статусный код 2хх) получаем отправленный сервером объект Person с установленным Id и этот Id выводим в сообщении:
if (response.IsSuccessStatusCode) { var newPerson = await response.Content.ReadFromJsonAsync<Person> (); if (newPerson != null) message = $"Создан объект Person с id = {newPerson.Id}"; }
Если же сервер возвратил ошибку (в нашем случае ошибку 400 при некорректности данных), получаем сообщение об ошибке в объект Error, и затем также выводим сообщение об ошибке на страницу:
else { var error = await response.Content.ReadFromJsonAsync<Error>(); if (error != null) message = error.Details; }
Пример работы: