--- title: Call a web API from an ASP.NET Core Blazor app author: guardrex description: Learn how to call a web API from Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 03/08/2024 uid: blazor/call-web-api --- # Call a web API from ASP.NET Core Blazor [!INCLUDE[](~/includes/not-latest-version.md)] This article describes how to call a web API from a Blazor app. ## Package The [`System.Net.Http.Json`](https://www.nuget.org/packages/System.Net.Http.Json) package provides extension methods for and that perform automatic serialization and deserialization using [`System.Text.Json`](https://www.nuget.org/packages/System.Text.Json). The `System.Net.Http.Json` package is provided by the .NET shared framework and doesn't require adding a package reference to the app. :::moniker range=">= aspnetcore-8.0" ## Sample apps See the sample apps in the [`dotnet/blazor-samples`](https://github.com/dotnet/blazor-samples/) GitHub repository. ### `BlazorWebAppCallWebApi` Call an external (not in the Blazor Web App) todo list web API from a Blazor Web App: * `Backend`: A web API app for maintaining a todo list, based on [Minimal APIs](xref:fundamentals/minimal-apis). The web API app is a separate app from the Blazor Web App, possibly hosted on a different server. * `BlazorApp`/`BlazorApp.Client`: A Blazor Web App that calls the web API app with an for todo list operations, such as creating, reading, updating, and deleting (CRUD) items from the todo list. For client-side rendering (CSR), which includes Interactive WebAssembly components and Auto components that have adopted CSR, calls are made with a preconfigured registered in the `Program` file of the client project (`BlazorApp.Client`): ```csharp builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.Configuration["FrontendUrl"] ?? "https://localhost:5002") }); ``` For server-side rendering (SSR), which includes prerendered and interactive Server components, prerendered WebAssembly components, and Auto components that are prerendered or have adopted SSR, calls are made with an registered in the `Program` file of the server project (`BlazorApp`): ```csharp builder.Services.AddHttpClient(); ``` Call an internal (inside the Blazor Web App) movie list API, where the API resides in the server project of the Blazor Web App: * `BlazorApp`: A Blazor Web App that maintains a movie list: * When operations are performed on the movie list within the app on the server, ordinary API calls are used. * When API calls are made by a web-based client, a web API is used for movie list operations, based on [Minimal APIs](xref:fundamentals/minimal-apis). * `BlazorApp.Client`: The client project of the Blazor Web App, which contains Interactive WebAssembly and Auto components for user management of the movie list. For CSR, which includes Interactive WebAssembly components and Auto components that have adopted CSR, calls to the API are made via a client-based service (`ClientMovieService`) that uses a preconfigured registered in the `Program` file of the client project (`BlazorApp.Client`). Because these calls are made over a public or private web, the movie list API is a *web API*. The following example obtains a list of movies from the `/movies` endpoint: ```csharp public class ClientMovieService(HttpClient http) : IMovieService { public async Task GetMoviesAsync(bool watchedMovies) { return await http.GetFromJsonAsync("movies") ?? []; } } ``` For SSR, which includes prerendered and interactive Server components, prerendered WebAssembly components, and Auto components that are prerendered or have adopted SSR, calls are made directly via a server-based service (`ServerMovieService`). The API doesn't rely on a network, so it's a standard API for movie list CRUD operations. The following example obtains a list of movies: ```csharp public class ServerMovieService(MovieContext db) : IMovieService { public async Task GetMoviesAsync(bool watchedMovies) { return watchedMovies ? await db.Movies.Where(t => t.IsWatched).ToArrayAsync() : await db.Movies.ToArrayAsync(); } } ``` ### `BlazorWebAppCallWebApi_Weather` A weather data sample app that uses streaming rendering for weather data. ### `BlazorWebAssemblyCallWebApi` Calls a todo list web API from a Blazor WebAssembly app: * `Backend`: A web API app for maintaining a todo list, based on [Minimal APIs](xref:fundamentals/minimal-apis). * `BlazorTodo`: A Blazor WebAssembly app that calls the web API with a preconfigured for todo list CRUD operations. :::moniker-end ## Server-side scenarios for calling external web APIs Server-based components call external web APIs using instances, typically created using . For guidance that applies to server-side apps, see . A server-side app doesn't include an service by default. Provide an to the app using the [`HttpClient` factory infrastructure](xref:fundamentals/http-requests). In the `Program` file: ```csharp builder.Services.AddHttpClient(); ``` The following Razor component makes a request to a web API for GitHub branches similar to the *Basic Usage* example in the article. `CallWebAPI.razor`: ```razor @page "/call-web-api" @using System.Text.Json @using System.Text.Json.Serialization @inject IHttpClientFactory ClientFactory

Call web API from a Blazor Server Razor component

@if (getBranchesError || branches is null) {

Unable to get branches from GitHub. Please try again later.

} else {
    @foreach (var branch in branches) {
  • @branch.Name
  • }
} @code { private IEnumerable? 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 >(responseStream); } else { getBranchesError = true; } shouldRender = true; } public class GitHubBranch { [JsonPropertyName("name")] public string? Name { get; set; } } } ``` In the preceding example for C# 12 or later, an empty array (`[]`) is created for the `branches` variable. For earlier versions of C#, create an empty array (`Array.Empty()`). For an additional working example, see the server-side file upload example that uploads files to a web API controller in the article. :::moniker range=">= aspnetcore-8.0" ## Service abstractions for web API calls *This section applies to Blazor Web Apps that maintain a web API in the server project or transform web API calls to an external web API.* When using the interactive WebAssembly and Auto render modes, components are prerendered by default. Auto components are also initially rendered interactively from the server before the Blazor bundle downloads to the client and the client-side runtime activates. This means that components using these render modes should be designed so that they run successfully from both the client and the server. If the component must call a server project-based API or transform a request to an external web API (one that's outside of the Blazor Web App) when running on the client, the recommended approach is to abstract that API call behind a service interface and implement client and server versions of the service: * The client version calls the web API with a preconfigured . * The server version can typically access the server-side resources directly. Injecting an on the server that makes calls back to the server is ***not*** recommended, as the network request is typically unnecessary. Alternatively, the API might be external to the server project, but a service abstraction for the server is required to transform the request in some way, for example to add an access token to a proxied request. When using the WebAssembly render mode, you also have the option of disabling prerendering, so the components only render from the client. For more information, see . Examples ([sample apps](#sample-apps)): * Movie list web API in the `BlazorWebAppCallWebApi` sample app. * Streaming rendering weather data web API in the `BlazorWebAppCallWebApi_Weather` sample app. * Weather data returned to the client in either the `BlazorWebAppOidc` (non-BFF pattern) or `BlazorWebAppOidcBff` (BFF pattern) sample apps. These apps demonstrate secure (web) API calls. For more information, see . ## Blazor Web App external web APIs *This section applies to Blazor Web Apps that call a web API maintained by a separate (external) project, possibly hosted on a different server.* Blazor Web Apps normally prerender client-side WebAssembly components, and Auto components render on the server during static or interactive server-side rendering (SSR). services aren't registered by default in a Blazor Web App's main project. If the app is run with only the services registered in the `.Client` project, as described in the [Add the `HttpClient` service](#add-the-httpclient-service) section, executing the app results in a runtime error: > :::no-loc text="InvalidOperationException: Cannot provide a value for property 'Http' on type '...{COMPONENT}'. There is no registered service of type 'System.Net.Http.HttpClient'."::: Use ***either*** of the following approaches: * Add the services to the server project to make the available during SSR. Use the following service registration in the server project's `Program` file: ```csharp builder.Services.AddHttpClient(); ``` No explicit package reference is required because services are provided by the shared framework. Example: Todo list web API in the `BlazorWebAppCallWebApi` [sample app](#sample-apps) * If prerendering isn't required for a WebAssembly component that calls the web API, disable prerendering by following the guidance in . If you adopt this approach, you don't need to add services to the main project of the Blazor Web App because the component won't be prerendered on the server. For more information, see [Client-side services fail to resolve during prerendering](xref:blazor/components/render-modes#client-side-services-fail-to-resolve-during-prerendering). ## Prerendered data When prerendering, components render twice: first statically, then interactively. State doesn't automatically flow from the prerendered component to the interactive one. If a component performs asynchronous initialization operations and renders different content for different states during initialization, such as a "Loading..." progress indicator, you may see a flicker when the component renders twice. You can address this by flowing prerendered state using the Persistent Component State API, which the `BlazorWebAppCallWebApi` and `BlazorWebAppCallWebApi_Weather` [sample apps](#sample-apps) demonstrate. When the component renders interactively, it can render the same way using the same state. However, the API doesn't currently work with enhanced navigation, which you can work around by disabling enhanced navigation on links to the page (`data-enhanced-nav=false`). For more information, see the following resources: * * * [Support persistent component state across enhanced page navigations (`dotnet/aspnetcore` #51584)](https://github.com/dotnet/aspnetcore/issues/51584) :::moniker-end ## Add the `HttpClient` service *The guidance in this section applies to client-side scenarios.* Client-side components call web APIs using a preconfigured service, which is focused on making requests back to the server of origin. Additional service configurations for other web APIs can be created in developer code. Requests are composed using Blazor JSON helpers or with . Requests can include [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) option configuration. The configuration examples in this section are only useful when a single web API is called for a single instance in the app. When the app must call multiple web APIs, each with its own base address and configuration, you can adopt the following approaches, which are covered later in this article: * [Named `HttpClient` with `IHttpClientFactory`](#named-httpclient-with-ihttpclientfactory): Each web API is provided a unique name. When app code or a Razor component calls a web API, it uses a named instance to make the call. * [Typed `HttpClient`](#typed-httpclient): Each web API is typed. When app code or a Razor component calls a web API, it uses a typed instance to make the call. In the `Program` file, add an service if it isn't already present from a Blazor project template used to create the app: ```csharp builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); ``` The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. The most common use cases for using the client's own base address are: * The client project (`.Client`) of a Blazor Web App (.NET 8 or later) makes web API calls from WebAssembly components or code that runs on the client in WebAssembly to APIs in the server app. * The client project (**:::no-loc text="Client":::**) of a hosted Blazor WebAssembly app makes web API calls to the server project (**:::no-loc text="Server":::**). Note that the hosted Blazor WebAssembly project template is no longer available in .NET 8 or later. However, hosted Blazor WebAssembly apps remain supported for .NET 8. If you're calling an external web API (not in the same URL space as the client app), set the URI to the web API's base address. The following example sets the base address of the web API to `https://localhost:5001`, where a separate web API app is running and ready to respond to requests from the client app: ```csharp builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); ``` ## JSON helpers is available as a preconfigured service for making requests back to the origin server. and JSON helpers () are also used to call third-party web API endpoints. is implemented using the browser's [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) and is subject to its limitations, including enforcement of the same-origin policy, which is discussed later in this article in the *Cross-Origin Resource Sharing (CORS)* section. The client's base address is set to the originating server's address. Inject an instance into a component using the [`@inject`](xref:mvc/views/razor#inject) directive: ```razor @using System.Net.Http @inject HttpClient Http ``` Use the namespace for access to , including , , and : ```razor @using System.Net.Http.Json ``` The following sections cover JSON helpers: * [GET](#get-from-json-getfromjsonasync) * [POST](#post-as-json-postasjsonasync) * [PUT](#put-as-json-putasjsonasync) * [PATCH](#patch-as-json-patchasjsonasync) includes additional methods for sending HTTP requests and receiving HTTP responses, for example to send a DELETE request. For more information, see the [DELETE and additional extension methods](#delete-deleteasync-and-additional-extension-methods) section. ## GET from JSON (`GetFromJsonAsync`) sends an HTTP GET request and parses the JSON response body to create an object. In the following component code, the `todoItems` are displayed by the component. is called when the component is finished initializing ([`OnInitializedAsync`](xref:blazor/components/lifecycle#component-initialization-oninitializedasync)). ```csharp todoItems = await Http.GetFromJsonAsync("todoitems"); ``` ## POST as JSON (`PostAsJsonAsync`) sends a POST request to the specified URI containing the value serialized as JSON in the request body. In the following component code, `newItemName` is provided by a bound element of the component. The `AddItem` method is triggered by selecting a `

Response body returned by the server:

@responseBody

@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's client-side implementation of uses [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch) and configures the underlying [request-specific Fetch API options](https://developer.mozilla.org/docs/Web/API/fetch#Parameters) via extension methods and . Set additional options using the generic extension method. Blazor and the underlying Fetch API don't directly add or modify request headers. For more information on how user agents, such as browsers, interact with headers, consult external user agent documentation sets and other web resources. The HTTP response is typically buffered to enable support for synchronous reads on the response content. To enable support for response streaming, use the extension method on the request. To include credentials in a cross-origin request, use the extension method: ```csharp requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include); ``` For more information on Fetch API options, see [MDN web docs: WindowOrWorkerGlobalScope.fetch(): Parameters](https://developer.mozilla.org/docs/Web/API/fetch#Parameters). ## Handle errors Handle web API response errors in developer code when they occur. For example, expects a JSON response from the web API with a `Content-Type` of `application/json`. If the response isn't in JSON format, content validation throws a . In the following example, the URI endpoint for the weather forecast data request is misspelled. The URI should be to `WeatherForecast` but appears in the call as `WeatherForcast`, which is missing the letter `e` in `Forecast`. The call expects JSON to be returned, but the web API returns HTML for an unhandled exception with a `Content-Type` of `text/html`. The unhandled exception occurs because the path to `/WeatherForcast` isn't found and middleware can't serve a page or view for the request. In on the client, is thrown when the response content is validated as non-JSON. The exception is caught in the `catch` block, where custom logic could log the error or present a friendly error message to the user. `ReturnHTMLOnException.razor`: ```razor @page "/return-html-on-exception" @using {PROJECT NAME}.Shared @inject HttpClient Http

Fetch data but receive HTML on unhandled exception

@if (forecasts == null) {

Loading...

} else {

Temperatures by Date

    @foreach (var forecast in forecasts) {
  • @forecast.Date.ToShortDateString(): @forecast.TemperatureC ℃ @forecast.TemperatureF ℉
  • }
}

@exceptionMessage

@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("WeatherForcast"); } catch (NotSupportedException exception) { exceptionMessage = exception.Message; } } } ``` > [!NOTE] > The preceding example is for demonstration purposes. A web API can be configured to return JSON even when an endpoint doesn't exist or an unhandled exception occurs on the server. For more information, see . ## Cross-Origin Resource Sharing (CORS) Browser security restricts a webpage from making requests to a different domain than the one that served the webpage. This restriction is called the *same-origin policy*. The same-origin policy restricts (but doesn't prevent) a malicious site from reading sensitive data from another site. To make requests from the browser to an endpoint with a different origin, the *endpoint* must enable [Cross-Origin Resource Sharing (CORS)](https://www.w3.org/TR/cors/). For more information on server-side CORS, see . The article's examples don't pertain directly to Razor component scenarios, but the article is useful for learning general CORS concepts. For information on client-side CORS requests, see . :::moniker range=">= aspnetcore-8.0" ## Antiforgery support To add antiforgery support to an HTTP request, inject the `AntiforgeryStateProvider` and add a `RequestToken` to the headers collection as a `RequestVerificationToken`: ```razor @inject AntiforgeryStateProvider Antiforgery ``` ```csharp 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); ... } ``` For more information, see . :::moniker-end ## Blazor framework component examples for testing web API access Various network tools are publicly available for testing web API backend apps directly, such as [Firefox Browser Developer](https://www.mozilla.org/firefox/developer/). Blazor framework's reference source includes test assets that are useful for testing: [`HttpClientTest` assets in the `dotnet/aspnetcore` GitHub repository](https://github.com/dotnet/aspnetcore/tree/main/src/Components/test/testassets/BasicTestApp/HttpClientTest) [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] ## Additional resources ### General * [Cross-Origin Resource Sharing (CORS) at W3C](https://www.w3.org/TR/cors/) * : Although the content applies to ASP.NET Core apps, not Razor components, the article covers general CORS concepts. ### Server-side * : Includes coverage on using to make secure web API requests. * * * [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel/endpoints) ### Client-side * : Includes coverage on using to make secure web API requests. * * [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch)