🟨 Slightly different to Blazor Server
Knowing how to interact with APIs is a crucial skill for working with Blazor WebAssembly. This involves sending requests to an API server and processing the resulting responses. In this tutorial, you will learn:
HttpClient
HttpClient
for a single APIHttpClient
for multiple APIsHttpClient
You can download the example code used in this topic on GitHub.
HttpClient
In Blazor WebAssembly projects, it's essential to register HttpClient
in order to perform server operations such as reading or writing files, accessing databases, and more. These operations must be performed at the API level, using technologies like ASP.NET Web API or ASP.NET gRPC Service. To use an API in a Blazor WebAssembly project, HttpClient
registration is required.
The process for registering APIs in Blazor WebAssembly can vary depending on how the API is designed. Factors to consider include whether there is a single API server or multiple servers, how authorization is handled at the API level, and how errors are handled in the API. By understanding these factors, you can determine the appropriate method for registering APIs in your Blazor WebAssembly project.
HttpClient
for a single APIIf your website has only one API, you can add HttpClient
as a scoped service in the Program.cs file. To do this, assuming your API endpoint is http://localhost:30911, use the following code:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://localhost:30911") });
Once registered, you can inject the HttpClient
into any razor component to make a request. For example:
@inject HttpClient HttpClient
This will allow you to interact with your API from within your Blazor WebAssembly application.
HttpClient
for multiple APIsWhen working with multiple APIs in Blazor WebAssembly, there are several ways to register HttpClient
, each with its own advantages and disadvantages. The three most common approaches are:
HttpClient
HttpClient
HttpClient
wrapperEach approach provides everything you need to interact with multiple APIs in your application, but the best approach will depend on the specific requirements of your project.
HttpClient
HttpClient
is a class that provides a simple API for sending and receiving HTTP requests and responses. In most scenarios, you can use HttpClient
directly to interact with remote services and APIs.
However, in some cases, you may need to customize the behavior of HttpClient
, such as adding custom headers, handling retries, or implementing a custom message handler. In these situations, you can derive a new class from HttpClient
and add your custom logic to it.
Deriving a new class from HttpClient
allows you to extend the functionality of HttpClient
while still using its core features and avoiding the need to write all the low-level HTTP code yourself. Your derived class can inherit all the methods and properties of HttpClient
and override or add new behavior as needed.
To create a derived class, follow these steps:
HttpClient
class and sets the BaseAddress
property in its constructor. For example:public class CustomHttpClient : HttpClient { public CustomHttpClient() { BaseAddress = new("http://localhost:30911"); } }
AddScoped
method of the IServiceCollection
interface. For example:builder.Services.AddScoped<CustomHttpClient>();
CustomHttpClient
class using the @inject
directive. For example:@inject CustomHttpClient CustomHttpClient
HttpClient
Named HttpClient
is a feature that allows you to create and manage multiple instances of the HttpClient
class or derived HttpClient
classes, each with its own set of configurations and settings, and distinguish between them using a unique name.
This is useful when your application needs to interact with multiple remote services or APIs, each with different requirements and characteristics, such as different timeouts, headers, base addresses, or authentication credentials. By giving each HttpClient
instance a name, you can easily refer to it when making HTTP requests, and the IHttpClientFactory
will take care of creating and configuring the appropriate client for you.
To create named HttpClient
instances, you can follow these steps:
builder.Services.AddHttpClient("First API", httpClient => httpClient.BaseAddress = new("http://localhost:30911")); builder.Services.AddHttpClient("Second API", httpClient => httpClient.BaseAddress = new("http://localhost:30912"));
IHttpClientFactory
interface and create an instance of HttpClient
by specifying its name. For example:@inject IHttpClientFactory HttpClientFactory ... @code { protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("First API"); ... } }
HttpClient
wrapper is a design pattern used to wrap the HttpClient
class in a custom class or interface to provide additional functionality or simplify its usage.
HttpClient
wrapperBy wrapping HttpClient
, you can encapsulate its implementation details and provide a simpler, more focused API that is easier to use and test. This can be especially useful when you need to add common headers or handle errors in a consistent way across multiple requests.
To create an HttpClient
wrapper, you can follow these steps:
HttpClient
as a dependency, like this:public class SecondApiHttpClientWrapper { private readonly HttpClient _httpClient; public SecondApiHttpClientWrapper(HttpClient httpClient) { _httpClient = httpClient; } }
builder.Services.AddHttpClient<SecondApiHttpClientWrapper>(httpClient => httpClient.BaseAddress = new("http://localhost:30912"));
@inject SecondApiHttpClientWrapper SecondApiHttpClientWrapper
HttpClient
Certain APIs require additional measures to interact with, such as protected JWT tokens, specific request headers, or loading indicators for requests and responses. In these cases, interfacing with HttpClient
can be beneficial. There are several methods to interface with HttpClient
:
HttpClient
middleware.HttpClient
wrapper.HttpClient
extension.HttpClient
middlewareThe HttpClient
middleware technique is built on the chain of responsibility pattern, where multiple middleware can be applied to an HTTP request and response in a specific order. The middleware chain starts with the first middleware and ends with the last middleware, processing the request and response respectively.
Follow these steps to create and use HttpClient
middleware:
DelegatingHandler
class and overrides the Send
/SendAsync
method. Interfering code can be added before and after sending the request.public class FirstMiddleware : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Interfering code before sending the request var response = await base.SendAsync(request, cancellationToken); // Interfering code after sending the request return response; } }
It's important to callbase.Send()
orbase.SendAsync()
when overriding theSend
orSendAsync
method respectively, to ensure the middleware chain continues without being broken.
builder.Services.AddTransient<FirstMiddleware>(); builder.Services.AddTransient<SecondMiddleware>();
HttpClient
by using named HttpClient
or HttpClient
wrapper technique, then add the middleware chain in your desired sequence.builder.Services.AddHttpClient("HttpClient with Middleware", httpClient => httpClient.BaseAddress = new("http://localhost:30911")) .AddHttpMessageHandler<FirstMiddleware>() .AddHttpMessageHandler<SecondMiddleware>(); builder.Services.AddHttpClient<SecondApiHttpClientWrapper>(httpClient => httpClient.BaseAddress = new("http://localhost:30912")) .AddHttpMessageHandler<FirstMiddleware>() .AddHttpMessageHandler<SecondMiddleware>();
HttpClient
wrapperHttpClient
wrapper is another way to interfere with HTTP requests and responses. You can provide additional methods to the HttpClient
wrapper to create a centralized interference code. Here is an example of how to create a method to send a request using a wrapper class:
public class InterfereByHttpClientWrapper { ... public async Task<T?> GetAsync<T>(string requestUrl) { // Interfering code before sending the request var response = await _httpClient.GetFromJsonAsync<T>(requestUrl); // Interfering code after sending the request return response; } }
HttpClient
extensionHttpClient
extension is another way to interfere with HTTP requests and responses. It allows you to add custom methods and functionality to the HttpClient
class.
To create a HttpClient
extension, you can create a static class with extension methods for the HttpClient
class. For example:
public static class HttpClientExtension { public static async Task<T?> GetResponse<T>(this HttpClient httpClient, string url) { // Interfering code before sending the request var result = await httpClient.GetFromJsonAsync<T>(url); // Interfering code after sending the request return result; } }
There are several common HTTP methods that are used to communicate with web servers. Here are some of the most common ones:
GET: This method is used to retrieve information from the server. It is the most commonly used HTTP method and is used to fetch web pages, images, videos, and other resources from the server.
POST: This method is used to submit data to the server. It is commonly used in web forms to send data to the server for processing.
PUT: This method is used to update or replace existing data on the server. It is commonly used in RESTful API's to update resources on the server.
DELETE: This method is used to delete a resource from the server. It is commonly used in RESTful API's to delete resources.
HEAD: This method is similar to the GET method, but it only retrieves the headers of the response, not the body. It is commonly used to check the status of a resource without downloading the entire response.
OPTIONS: This method is used to retrieve the supported HTTP methods for a resource. It is commonly used in CORS (Cross-Origin Resource Sharing) to check whether a particular origin is allowed to access a resource.
PATCH: This method is used to make partial updates to a resource. It is commonly used in RESTful API's to update specific fields of a resource without replacing the entire resource.
You can find out more at https://www.w3schools.com/tags/ref_httpmethods.asp.
It is possible to send data to an API by including it in the URL, a commonly used method with the GET request. However, it is important to note that the data sent through the URL should be a short, primitive data type. It is not recommended to send large data sets through the URL. Instead, HttpClient
can be utilized to send data through the URL, as shown below:
await httpClient.GetAsync($"example/ProcessPrimitiveUrlData?data=Blazor School");
When sending primitive data in an HTTP request, the data can be included in the request body rather than in the URL. This is useful when the data being sent is larger or more complex than a simple query parameter. To send primitive data in the request body, the following code can be used:
await httpClient.PostAsync("example/ProcessPrimitiveData", new StringContent("Blazor School", Encoding.UTF8, "application/json"));
This will encode the data as a JSON string and send it to the specified API endpoint for processing.
Sending complex data in the request body is a common practice in web development when exchanging data between a client (such as a web browser) and a server. Complex data can include objects, arrays, and nested data structures.
When sending complex data in the request body, the data is usually serialized into a format that can be transmitted over the network. There are several formats for serializing data, including JSON, XML, and YAML, among others. JSON (JavaScript Object Notation) is a widely used format for serializing complex data, and it is supported by most modern web development frameworks.
There are several methods available in the System.Net.Http.Json
namespace that can assist in sending complex data in the request body. As an example, the following code snippet demonstrates the usage of the PostAsJsonAsync
method to send a complex object in the request body:
var data = new ExampleClass() { ExampleString = "Blazor School" }; await httpClient.PostAsJsonAsync<ExampleClass>("example/ProcessComplexData", data);
Sending stream data in the request body is a technique used to transmit large or continuous data streams from the client to the server over an HTTP connection. This technique is commonly used in scenarios where data is generated or consumed in real-time, such as video streaming, file uploading, or real-time sensor data.
When sending stream data in the request body, the client typically uses the HTTP POST method to send a series of data chunks to the server. The data chunks are sent in a continuous stream, without waiting for a response from the server after each chunk is sent.
The most common use case for sending stream data is to upload a file to an API. In Blazor, the InputFile
component can be used to prompt the user to select a file. The selected file is then mapped to the IBrowserFile
interface by the InputFile
component. The file can be converted to StreamContent
and sent to the server using the HttpClient
class. Here is an example of how to upload a file to an API using the InputFile
component in Blazor:
public class ExampleFormModel { public IBrowserFile ExampleFile { get; set; } = default!; }
InputFile
component:@inject IHttpClientFactory HttpClientFactory <EditForm Model="FormModel" OnSubmit="SubmitFormAsync"> <InputFile OnChange="FileChanged" /> <button>Submit</button> </EditForm> @code { public ExampleFormModel FormModel { get; set; } = new(); public void FileChanged(InputFileChangeEventArgs args) { FormModel.ExampleFile = args.File; } public async Task SubmitFormAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); using var formDataContent = new MultipartFormDataContent(); using var stream = FormModel.ExampleFile.OpenReadStream(long.MaxValue); using var streamContent = new StreamContent(stream); streamContent.Headers.ContentType = new(FormModel.ExampleFile.ContentType); formDataContent.Add(streamContent, "FileStream", FormModel.ExampleFile.Name); var message = await httpClient.PostAsync("example/ProcessStreamdata", formDataContent); // Further logic } }
In the above example, the formDataContent
only contains a single file. However, you can add multiple files to the formDataContent
by calling the Add
method on the content object multiple times, each time with a different file and a unique name. This way, you can send multiple files in a single request to the server.
After sending a request to the API, the API will process the request and return a response to the HttpClient
. Sometimes, the response will contain data, while other times it won't. To handle a response that contains data, you can use the following code:
var response = await httpClient.GetAsync("example/ReturnPrimitiveData"); if(response.IsSuccessStatusCode) { DataReceived = await response.Content.ReadAsStringAsync(); } else { DataReceived = "Failed"; }
You can automatically deserialize JSON response data to a C# object using the Microsoft library System.Net.Http.Json
. This library leverages System.Text.Json
to handle the deserialization. Here's an example implementation:
@inject IHttpClientFactory HttpClientFactory <div>Data Received: @ExampleInstance.ExampleString</div> @code { public ExampleClass ExampleInstance { get; set; } = new(); protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); ExampleInstance = await httpClient.GetFromJsonAsync<ExampleClass>("example/ReturnComplexData") ?? new(); } }
Newtonsoft.Json
is a popular third-party library for working with JSON data in .NET applications. It provides a comprehensive set of classes and methods for serializing and deserializing JSON data to and from .NET objects, as well as working with JSON documents directly. Newtonsoft.Json
is often considered more user-friendly and flexible than System.Text.Json
, with a wider range of configuration options and a more intuitive API. Here's an example implementation:
@using Newtonsoft.Json @inject IHttpClientFactory HttpClientFactory <div>Data Received: @ExampleInstance.ExampleString</div> @code { public ExampleClass ExampleInstance { get; set; } = new(); protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); var response = await httpClient.GetAsync("example/ReturnComplexData"); if(response.IsSuccessStatusCode) { string responseContent = await response.Content.ReadAsStringAsync(); ExampleInstance = JsonConvert.DeserializeObject<ExampleClass>(responseContent) ?? new(); } } }
To avoid repeating the same code, you can follow one of the techniques in the Interfere HttpClient section.
When an API returns a stream, you can process it using the ReadAsStreamAsync()
method. Here's an example code that processes an image file stream:
@inject IHttpClientFactory HttpClientFactory <img src="@ImageSrc"/> @code { public string ImageSrc { get; set; } = ""; protected override async Task OnInitializedAsync() { var httpClient = HttpClientFactory.CreateClient("Second API"); var response = await httpClient.GetAsync("example/ReturnStreamData"); if(response.IsSuccessStatusCode) { using var stream = await response.Content.ReadAsStreamAsync(); byte[] buffer = new byte[stream.Length]; await stream.ReadAsync(buffer, 0, (int)stream.Length); string base64 = Convert.ToBase64String(buffer); ImageSrc = $"data:image/png;base64,{base64}"; } StateHasChanged(); } }
Note that you need to process the stream according to its type, and it may not always be an image file stream as in the example.
In Blazor WebAssembly, requests are sent from the browser, and they can contain client-side information such as cookies.
On the other hand, in Blazor Server, requests are sent from the server, and they do not contain client-side information.