--- 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: 04/06/2023 uid: blazor/call-web-api zone_pivot_groups: blazor-hosting-models --- # 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. > [!NOTE] > The code examples in this article adopt [nullable reference types (NRTs) and .NET compiler null-state static analysis](xref:migration/50-to-60#nullable-reference-types-nrts-and-net-compiler-null-state-static-analysis), which are supported in ASP.NET Core 6.0 or later. When targeting ASP.NET Core 5.0 or earlier, remove the null type designation (`?`) from the `string?`, `TodoItem[]?`, `WeatherForecast[]?`, and `IEnumerable?` types in the article's examples. :::zone pivot="webassembly" > [!NOTE] > This article has loaded **Blazor WebAssembly** coverage for calling web APIs. The [Blazor Server coverage](?pivots=server) addresses the following subjects: > > * Use of the `HttpClient` factory infrastructure to provide an `HttpClient` to the app. > * Cross-origin resource sharing (CORS) pertaining to Blazor Server apps. > * Blazor framework component examples for testing web API access. > * Additional resources for developing Blazor Server apps that call a web API. [Blazor WebAssembly](xref:blazor/hosting-models#blazor-webassembly) apps 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. ## Examples in this article In this article's component examples, a hypothetical todo list web API is used to create, read, update, and delete (CRUD) todo items on a server. The examples are based on a `TodoItem` class that stores the following todo item data: * ID (`Id`, `long`): Unique ID of the item. * Name (`Name`, `string`): Name of the item. * Status (`IsComplete`, `bool`): Indication if the todo item is finished. Use the following `TodoItem` class with this article's examples if you build the examples into a test app: ```csharp public class TodoItem { public long Id { get; set; } public string? Name { get; set; } public bool IsComplete { get; set; } } ``` For guidance on how to create a server-side web API, see . For information on Cross-origin resource sharing (CORS), see the *Cross-origin resource sharing (CORS)* section later in this article. The Blazor WebAssembly examples that demonstrate obtaining weather data from a server API are based on a hosted Blazor WebAssembly solution created from the [Blazor WebAssembly project template](xref:blazor/project-structure#blazor-webassembly). ## 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`](https://www.nuget.org/packages/System.Net.Http.Json) package is provided by the .NET shared framework and doesn't require adding a package reference to the app. ## Add the `HttpClient` service In `Program.cs`, 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) }); ``` ## `HttpClient` and 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 ``` ### 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)). > [!NOTE] > When targeting ASP.NET Core 5.0 or earlier, add `@using` directives to the following component for , , and . ```razor @inject HttpClient Http @if (todoItems == null) {

No Todo Items found.

} else {
    @foreach (var item in todoItems) {
  • @item.Name
  • }
} @code { private TodoItem[]? todoItems; protected override async Task OnInitializedAsync() => todoItems = await Http.GetFromJsonAsync("api/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 ` @code { private string? newItemName; private async Task AddItem() { var addItem = new TodoItem { Name = newItemName, IsComplete = false }; await Http.PostAsJsonAsync("api/TodoItems", addItem); } } ``` returns an . To deserialize the JSON content from the response message, use the extension method. The following example reads JSON weather data as an array: :::moniker range=">= aspnetcore-6.0" ```csharp var content = await response.Content.ReadFromJsonAsync() ?? Array.Empty(); ``` In the preceding example, an empty array is created if no weather data is returned by the method, so `content` isn't null after the statement executes. :::moniker-end :::moniker range="< aspnetcore-6.0" ```csharp var content = await response.Content.ReadFromJsonAsync(); ``` :::moniker-end ### PUT as JSON (`PutAsJsonAsync`) sends an HTTP PUT request with JSON-encoded content. In the following component code, `editItem` values for `Name` and `IsCompleted` are provided by bound elements of the component. The item's `Id` is set when the item is selected in another part of the UI (not shown) and `EditItem` is called. The `SaveItem` method is triggered by selecting the ` @code { private string? id; private TodoItem editItem = new TodoItem(); private void EditItem(long id) { editItem = todoItems.Single(i => i.Id == id); } private async Task SaveItem() => await Http.PutAsJsonAsync($"api/TodoItems/{editItem.Id}", editItem); } ``` returns an . To deserialize the JSON content from the response message, use the extension method. The following example reads JSON weather data as an array: :::moniker range=">= aspnetcore-6.0" ```csharp var content = await response.Content.ReadFromJsonAsync() ?? Array.Empty(); ``` In the preceding example, an empty array is created if no weather data is returned by the method, so `content` isn't null after the statement executes. :::moniker-end :::moniker range="< aspnetcore-6.0" ```csharp var content = await response.Content.ReadFromJsonAsync(); ``` :::moniker-end :::moniker range=">= aspnetcore-7.0" ### PATCH as JSON (`PatchAsJsonAsync`) sends an HTTP PATCH request with JSON-encoded content. In the following component code: * `incompleteTodoItems` is an array of incomplete `TodoItem`. The following example doesn't show loading `incompleteTodoItems` for brevity. See the [GET from JSON (`GetFromJsonAsync`)](#get-from-json-getfromjsonasync) section for an example of loading items. * The `UpdateItem` method is triggered by selecting the ` } @code { private async Task UpdateItem(long id) => await Http.PatchAsJsonAsync( $"api/TodoItems/{id}", "[{\"operationType\":2,\"path\":\"/IsComplete\",\"op\":\"replace\",\"value\":true}]"); } ``` returns an . To deserialize the JSON content from the response message, use the extension method. The following example reads JSON weather data as an array. An empty array is created if no weather data is returned by the method, so `content` isn't null after the statement executes: ```csharp var content = await response.Content.ReadFromJsonAsync() ?? Array.Empty(); ``` receives a JSON PATCH document for the PATCH request. The preceding `UpdateItem` method called with a PATCH document as a string with escaped quotes. Laid out with indentation, spacing, and non-escaped quotes, the unencoded PATCH document appears as the following JSON: ```json [ { "operationType": 2, "path": "/IsComplete", "op": "replace", "value": true } ] ``` To simplify the creation of PATCH documents in the app issuing PATCH requests, an app can use .NET JSON PATCH support, as the following guidance demonstrates. Install the [`Microsoft.AspNetCore.JsonPatch`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch) NuGet package and use the API features of the package to compose a for a PATCH request. [!INCLUDE[](~/includes/package-reference.md)] Add an `@using` directive for the namespace to the top of the Razor component: ```razor @using Microsoft.AspNetCore.JsonPatch ``` Compose the for a `TodoItem` with `IsComplete` set to `true` using the method: ```csharp var patchDocument = new JsonPatchDocument() .Replace(p => p.IsComplete, true); ``` Pass the document's operations (`patchDocument.Operations`) to the call. The following example shows how to make the call: ```csharp private async Task UpdateItem(long id) { await Http.PatchAsJsonAsync( $"api/TodoItems/{id}", patchDocument.Operations, new JsonSerializerOptions() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, WriteIndented = true }); } ``` is set to to ignore a property only if it equals the default value for its type. is used merely to present the JSON payload in a pleasant format for this article. Writing indented JSON has no bearing on processing PATCH requests and isn't typically performed in production apps for web API requests. Next, follow the guidance in the article to add a PATCH controller action to the web API. Add a package reference for the [`Microsoft.AspNetCore.Mvc.NewtonsoftJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson) NuGet package to the web API app. > [!NOTE] > There's no need to add a package reference for the [`Microsoft.AspNetCore.JsonPatch`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch) package to the app because the reference to the `Microsoft.AspNetCore.Mvc.NewtonsoftJson` package automatically transitively adds a package reference for `Microsoft.AspNetCore.JsonPatch`. Add a custom JSON PATCH input formatter to the web API app. `JSONPatchInputFormatter.cs`: ```csharp using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.Options; public static class JSONPatchInputFormatter { public static NewtonsoftJsonPatchInputFormatter Get() { var builder = new ServiceCollection() .AddLogging() .AddMvc() .AddNewtonsoftJson() .Services.BuildServiceProvider(); return builder .GetRequiredService>() .Value .InputFormatters .OfType() .First(); } } ``` Configure the web API's controllers to use the `Microsoft.AspNetCore.Mvc.NewtonsoftJson` package and process PATCH requests with the JSON PATCH input formatter. Insert the `JSONPatchInputFormatter` in the first position of MVC's input formatter collection so that it processes requests prior to any other input formatter. In `Program.cs` modify the call to : ```csharp builder.Services.AddControllers(options => { options.InputFormatters.Insert(0, JSONPatchInputFormatter.Get()); }).AddNewtonsoftJson(); ``` In `Controllers/TodoItemsController.cs`, add a `using` statement for the namespace: ```csharp using Microsoft.AspNetCore.JsonPatch; ``` In `Controllers/TodoItemsController.cs`, add the following `PatchTodoItem` action method: ```csharp [HttpPatch("{id}")] public async Task PatchTodoItem(long id, JsonPatchDocument patchDoc) { if (patchDoc == null) { return BadRequest(); } var todoItem = await _context.TodoItems.FindAsync(id); if (todoItem == null) { return NotFound(); } patchDoc.ApplyTo(todoItem); _context.Entry(todoItem).State = EntityState.Modified; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) when (!TodoItemExists(id)) { return NotFound(); } return NoContent(); } ``` > [!WARNING] > As with the other examples in the article, the preceding PATCH controller action doesn't protect the web API from over-posting attacks. For more information, see . :::moniker-end ### Additional extension methods includes additional extension methods for sending HTTP requests and receiving HTTP responses. is used to send an HTTP DELETE request to a web API. In the following component code, the ` @code { private long id; private async Task DeleteItem() => await Http.DeleteAsync($"api/TodoItems/{id}"); } ``` ## Named `HttpClient` with `IHttpClientFactory` services and the configuration of a named are supported. > [!NOTE] > An alternative to using a named from an is to use a typed . For more information, see the [Typed `HttpClient`](#typed-httpclient) section. Add the [`Microsoft.Extensions.Http`](https://www.nuget.org/packages/Microsoft.Extensions.Http) NuGet package to the app. [!INCLUDE[](~/includes/package-reference.md)] In `Program.cs`: ```csharp builder.Services.AddHttpClient("WebAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); ``` In the following component code: * An instance of creates a named . * The named is used to issue a GET request for JSON weather forecast data from the web API. > [!NOTE] > When targeting ASP.NET Core 5.0 or earlier, add `@using` directives to the following component for , , and . `Pages/FetchDataViaFactory.razor`: ```razor @page "/fetch-data-via-factory" @using {PROJECT NAME}.Shared @inject IHttpClientFactory ClientFactory

Fetch data via IHttpClientFactory

@if (forecasts == null) {

Loading...

} else {

Temperatures by Date

    @foreach (var forecast in forecasts) {
  • @forecast.Date.ToShortDateString(): @forecast.TemperatureC ℃ @forecast.TemperatureF ℉
  • }
} @code { private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { var client = ClientFactory.CreateClient("WebAPI"); forecasts = await client.GetFromJsonAsync( "WeatherForecast"); } } ``` ## Typed `HttpClient` Typed uses one or more of the app's instances, default or named, to return data from one or more web API endpoints. > [!NOTE] > An alternative to using a typed is to use a named from an . For more information, see the [Named `HttpClient` with `IHttpClientFactory`](#named-httpclient-with-ihttpclientfactory) section. Add the [`Microsoft.Extensions.Http`](https://www.nuget.org/packages/Microsoft.Extensions.Http) NuGet package to the app. [!INCLUDE[](~/includes/package-reference.md)] `WeatherForecastHttpClient.cs`: ```csharp using System.Net.Http.Json; using {PROJECT NAME}.Shared; public class WeatherForecastHttpClient { private readonly HttpClient http; private WeatherForecast[]? forecasts; public WeatherForecastHttpClient(HttpClient http) { this.http = http; } public async Task GetForecastAsync() { forecasts = await http.GetFromJsonAsync( "WeatherForecast"); return forecasts ?? Array.Empty(); } } ``` In `Program.cs`: ```csharp builder.Services.AddHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); ``` Components inject the typed to call the web API. In the following component code: * An instance of the preceding `WeatherForecastHttpClient` is injected, which creates a typed . * The typed is used to issue a GET request for JSON weather forecast data from the web API. > [!NOTE] > When targeting ASP.NET Core 5.0 or earlier, add an `@using` directive to the following component for . `Pages/FetchDataViaTypedHttpClient.razor`: ```razor @page "/fetch-data-via-typed-httpclient" @using {PROJECT NAME}.Shared @inject WeatherForecastHttpClient Http

Fetch data via typed HttpClient

@if (forecasts == null) {

Loading...

} else {

Temperatures by Date

    @foreach (var forecast in forecasts) {
  • @forecast.Date.ToShortDateString(): @forecast.TemperatureC ℃ @forecast.TemperatureF ℉
  • }
} @code { private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { forecasts = await Http.GetForecastAsync(); } } ``` ## `HttpClient` and `HttpRequestMessage` with Fetch API request options [`HttpClient`](xref:fundamentals/http-requests) ([API documentation](xref:System.Net.Http.HttpClient)) and can be used to customize requests. For example, you can specify the HTTP method and request headers. The following component makes a `POST` request to a web API endpoint and shows the response body. > [!NOTE] > When targeting ASP.NET Core 5.0 or earlier, add `@using` directives to the following component for and . `Pages/TodoRequest.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-web-api/TodoRequest.razor"::: Blazor WebAssembly's implementation of uses [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch). Fetch API allows the configuration of several [request-specific options](https://developer.mozilla.org/docs/Web/API/fetch#Parameters). Options can be configured with extension methods shown in the following table. | Extension method | Fetch API request property | | --- | --- | | | [`cache`](https://developer.mozilla.org/docs/Web/API/Request/cache) | | | [`credentials`](https://developer.mozilla.org/docs/Web/API/Request/credentials) | | | [`integrity`](https://developer.mozilla.org/docs/Web/API/Request/integrity) | | | [`mode`](https://developer.mozilla.org/docs/Web/API/Request/mode) | Set additional options using the generic extension method. 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). ## Call web API example The following example calls a web API. The example requires a running web API based on the sample app described by the article. This example makes requests to the web API at `https://localhost:10000/api/TodoItems`. If a different web API address is used, update the `ServiceEndpoint` constant value in the component's `@code` block. The following example makes a [cross-origin resource sharing (CORS)](xref:security/cors) request from `http://localhost:5000` or `https://localhost:5001` to the web API. Add the following CORS Middleware configuration to the web API's service's `Program.cs` file: ```csharp app.UseCors(policy => policy.WithOrigins("http://localhost:5000", "https://localhost:5001") .AllowAnyMethod() .WithHeaders(HeaderNames.ContentType)); ``` Adjust the domains and ports of `WithOrigins` as needed for the Blazor app. For more information, see . By default, ASP.NET Core apps use ports 5000 (HTTP) and 5001 (HTTPS). To run both apps on the same machine at the same time for testing, use a different port for the web API app (for example, port 10000). For more information on setting the port, see . `Pages/CallWebAPI.razor`: :::code language="razor" source="~/../blazor-samples/7.0/BlazorSample_WebAssembly/Pages/call-web-api/CallWebAPI.razor"::: ## 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. > [!NOTE] > When targeting ASP.NET Core 5.0 or earlier, add `@using` directives to the following component for , , and . `Pages/FetchDataReturnsHTMLOnException.razor`: ```razor @page "/fetch-data-returns-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 . :::zone-end :::zone pivot="server" > [!NOTE] > This article has loaded **Blazor Server** coverage for calling web APIs. The [Blazor WebAssembly coverage](?pivots=webassembly) addresses the following subjects: > > * Blazor WebAssembly examples based on an client-side WebAssembly app that calls a web API to create, read, update, and delete todo list items. > * `System.Net.Http.Json` package. > * `HttpClient` service configuration. > * `HttpClient` and JSON helpers (`GetFromJsonAsync`, `PostAsJsonAsync`, `PutAsJsonAsync`, `DeleteAsync`). > * `IHttpClientFactory` services and the configuration of a named `HttpClient`. > * Typed `HttpClient`. > * `HttpClient` and `HttpRequestMessage` to customize requests. > * Call web API example with cross-origin resource sharing (CORS) and how CORS pertains to Blazor WebAssembly apps. > * How to handle web API response errors in developer code. > * Blazor framework component examples for testing web API access. > * Additional resources for developing Blazor WebAssembly apps that call a web API. [Blazor Server](xref:blazor/hosting-models#blazor-server) apps call web APIs using instances, typically created using . For guidance that applies to Blazor Server, see . A Blazor Server app doesn't include an service by default. Provide an to the app using the [`HttpClient` factory infrastructure](xref:fundamentals/http-requests). In `Program.cs`: ```csharp builder.Services.AddHttpClient(); ``` The following Blazor Server Razor component makes a request to a web API for GitHub branches similar to the *Basic Usage* example in the article. `Pages/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 = Array.Empty(); 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; } } } ``` For an additional working example, see the Blazor Server file upload example that uploads files to a web API controller in the article. :::zone-end ## 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/). :::zone pivot="webassembly" For information on CORS requests in Blazor WebAssembly apps, see . For information on CORS, see . The article's examples don't pertain directly to Blazor WebAssembly apps, but the article is useful for learning general CORS concepts. :::zone-end :::zone pivot="server" For more information, see . :::zone-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/) and [Postman](https://www.postman.com). 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 :::zone pivot="webassembly" * : Includes coverage on using to make secure web API requests. * : Although the content applies to ASP.NET Core apps, not Blazor WebAssembly apps, the article covers general CORS concepts. * [Cross-Origin resource sharing (CORS) at W3C](https://www.w3.org/TR/cors/) * [Fetch API](https://developer.mozilla.org/docs/Web/API/fetch) :::zone-end :::zone pivot="server" * : Includes coverage on using to make secure web API requests. * * * * [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel/endpoints) * [Cross-Origin resource sharing (CORS) at W3C](https://www.w3.org/TR/cors/) :::zone-end