See how to integrate third-party APIs in Blazor WebAssembly applications, dealing with CORS issues and error handling.
Blazor WebAssembly has been involved in a first-class web framework for building interactive web applications using C# and .NET.
Besides offering simple and powerful APIs for user interface rendering, Blazor also integrates with internal and external APIs. In this article, we will explore how to consume APIs from Blazor WebAssembly and how to deal with common problems, such as CORS and error handling.
You can access the code used in this example on GitHub.
The HttpClient
type provides access to APIs. First, we need to register the HttpClient
with the Client
project (WebAssembly) and (because of prerendering) with the Server
project (ASP.NET Core).
Server project:
builder.Services.AddHttpClient();
Client project:
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
}
);
In the Client
project, we use the HostEnvironment
property on the builder
object to access the BaseAddress
of the Server
project and set it as the BaseAddress
of the HttpClient
object. It allows us to provide relative paths in the Client
project when accessing APIs from the Server
project.
Now we use the @inject
directive to inject an instance of the HttpClient
type in the Weather
page component in the Client
project:
@inject HttpClient HttpClient
In the code section, we can now use the HttpClient
object and its GetAsync
method to load data from the /api/weather
route.
Note: If you want to learn how to implement the server-side Controller that provides the API, you can access the code used in this example on GitHub.
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
var response = await HttpClient.GetAsync("/api/weather");
var weatherForecasts =
await response.Content
.ReadFromJsonAsync<IEnumerable<WeatherForecast>>();
forecasts = weatherForecasts?.ToArray();
}
}
After loading the data using the GetAsync
method, we use the ReadFromJsonAsync
method and provide a generic type argument of the type we expect from the API.
Finally, we assign the returned value to the private forecasts
field to render the data on the page.
One of the best things about using the HttpClient
for accessing APIs is that we can use the same type and the same methods to load data from internal APIs (Server project) and from remote APIs (foreign services).
Depending on your preference, you can register an HttpClient
per external API and inject the respective instance to your Blazor components.
Alternatively, you can use the same instance for all API calls and provide a relative path for internal and an absolute path for external APIs.
Some more advanced services, such as Sentry, provide a dedicated SDK for .NET development.
The advantage is that the SDK provides a native interface, such as types and methods that represent operations, instead of having to implement API calls using the HttpClient
ourselves.
The integration of those APIs is simpler than accessing APIs using the HttpClient
type. However, the HttpClient
type provides a generic interface that allows you to access any HTTP API, including those without a dedicated SDK.
In the end, most dedicated SDKs use the HttpClient
type in their implementation and provide wrapper methods around it.
When making API calls from web applications, we often hit Cross-Origin Resource Sharing (CORS) issues.
Since Blazor WebAssembly runs in the browser, the browser’s same-origin policy applies. You might encounter CORS errors if the API server doesn’t explicitly allow requests from your application’s origin.
The simplest solution to resolving CORS issues is to configure the server correctly. In the case of an ASP.NET Core server application, we use the AddCors
middleware and configure it accordingly.
services.AddCors(options =>
{
options.AddPolicy("BlazorClientApp",
builder => builder.WithOrigins("https://blazor-wasm-app.com")
.AllowAnyMethod()
.AllowAnyHeader());
});
app.UseCors("AllowBlazorApp");
We add a named policy and allowed the client application to access the server. We have full control over the HTTP methods and headers we want to allow or forbid.
If we do not have control over the server, we need to implement a server-side proxy.
We implement a wrapper API (API Proxy) on the server that consumes the third-party API and provides it to the client application. Since we control the server application, we can correctly configure the server application to allow connections from the Blazor WebAssembly client application.
It works because CORS is a client-side browser technology. If we connect to the API on the server, CORS does not apply. However, be aware that the API still has to allow access. For example, you need to provide an API key or another form of authentication.
Asynchronous programming is the default in modern web development, and Blazor WebAssembly is no different.
Properly executing and managing API calls is essential for a smooth user experience.
The simplest starting point is adding try-catch
statements around HttpClient
calls to gracefully handle errors such as network failures or invalid HTTP responses.
try
{
var url = "https://api.example.com/data";
data = await Http.GetFromJsonAsync<List<string>>(url);
}
catch (Exception ex)
{
error = ex.Message;
}
This code allows you to provide a custom error message to the user and log the error in your application logs.
Logging errors helps you find those errors that often occur. Without logging errors, you might not even notice mistakes that happen in your application.
Loading indicators allow you to notify the user that data is being fetched. This prevents the application from appearing unresponsive during longer operations, such as API calls.
There are several ways to implement a loading indicator when working with Blazor. I prefer the following approach:
private async Task LoadCustomers()
{
_isLoading = true;
StateHasChanged();
_customers = await CustomerService.GetAllCustomers();
_isLoading = false;
StateHasChanged();
}
I define a private field of type bool
to hold the information on whether the data is loading. I also implement an async
Load
method and set the _isLoading
field to true
. Next, I call the StateHasChanged
method to make sure that the loading indicator shows on the screen.
You can implement a custom loading indicator and show it depending on the value of the _isLoading
field, or you can use one of the existing components, such as the Blazor Loader from Progress Telerik UI for Blazor.
Some APIs limit the requests a client can send in a given time frame.
Implementing debouncing or throttling is an excellent strategy to minimize the number of requests sent within a short period.
Some libraries like Polly and Reactive Extensions (Rx.NET) help you in such a situation.
Modern web applications consume internal and external APIs. The web is connected, and our applications don’t work in an isolated silo.
The HttpClient
type provides a modern, robust and performant asynchronous interface to access APIs using the HTTP protocol.
CORS is a common problem when working with APIs. Make sure to set the correct CORS headers by configuring your ASP.NET Core backend service. Implementing a server-side proxy might be the only option if you consume a third-party API.
Make sure to handle errors using a try-catch
statement when using the HttpClient
type to access APIs and use loading indicators to improve the user experience of your web application.
Polly helps with debouncing, throttling and retrying HTTP requests.
If you want to learn more about Blazor development, you can watch my free Blazor Crash Course on YouTube. And stay tuned to the Telerik blog for more Blazor Basics.
Claudio Bernasconi is a passionate software engineer and content creator writing articles and running a .NET developer YouTube channel. He has more than 10 years of experience as a .NET developer and loves sharing his knowledge about Blazor and other .NET topics with the community.