Конфигурация CORS

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 22.12.2019

Для работы с CORS в прошлой теме применялся следующий класс Startup:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace CorsApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(); // добавляем сервисы CORS
        }

        public void Configure(IApplicationBuilder app)
        {
            
            app.UseDeveloperExceptionPage();

            app.UseRouting();

            // подключаем CORS
            app.UseCors(builder => builder.AllowAnyOrigin());

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            });
        }
    }
}

В вызове app.UseCors с помощью методов объекта CorsPolicyBuilder можно настроить конфигурацию CORS:

  • AllowAnyOrigin(): принимаются запросы с любого адреса

  • AllowAnyHeader(): принимаются запросы с любыми заголовками

  • AllowAnyMethod(): принимаются запросы любого типа (GET/POST)

  • AllowCredentials(): разрешается принимать идентификационные данные от клиента (например, куки)

  • WithHeaders(): принимаются только те запросы, которые используют содержат определенные заголовки

  • WithMethods(): принимаются запросы только определенного типа

  • WithOrigins(): принимаются запросы только с определенных адресов

  • WithExposedHeaders(): позволяет серверу отправлять на сторону клиента свои заголовки

Определение адреса

Метод AllowAnyOrigin() позволяет установить взаимодействие с любым приложением по любому адресу. Однако подобное поведение может быть нежелательным. В этом случае мы можем ограничить круг адресов с помощью метода WithOrigins():

app.UseCors(builder => builder.WithOrigins("http://example.com", "http://google.com"));

При чем, что важно, в конце названия домена не должно быть конечного слеша.

Определение метода запроса

Метод AllowAnyMethod() позволяет принимать запросы любого типа (GET/POST). Также можно настроить принятие только определенного типа запросов:

app.UseCors(builder => builder.WithOrigins("https://localhost:44321").WithMethods("GET"));

Определение заголовков

Для разрешения запросов с любыми заголовками применяется метод AllowAnyHeader(). Следует отметить, что вместе с этим методом лучше также указывать и метод AllowAnyMethod() или WithMethods() для указания типа запроса:

app.UseCors(builder => builder.WithOrigins("https://localhost:44321")
							.AllowAnyHeader()
                            .AllowAnyMethod());

Если необходимо принимать запросы только с определенными заголовоками, то все требуемые заголовки надо передать в метод WithHeaders():

app.UseCors(builder => builder.WithOrigins("https://localhost:44321")
							.AllowAnyMethod()
							.WithHeaders("accept", "content-type", "origin", "custom-header"));

Если в метод передаются заголовки, отличные от "*", то следует указать по крайней мере три заголовка "accept", "content-type", "origin", а также какие-то свои заголовки, например, как в данном случае "custom-header".

Отправка заголовков

Если сервер отправляет какие-то свои заголовки, то по умолчанию клиент их не получает. Чтобы на стороне сервера указать, какие заголовки может получать клиент, следует использовать метод WithExposedHeaders():

public void Configure(IApplicationBuilder app)
{
	app.UseCors(builder => builder.WithOrigins("https://localhost:44321")
								.AllowAnyMethod()
                                .AllowAnyHeader()
                                .WithExposedHeaders("custom-header"));
	app.Run(async (context) =>
	{
		context.Response.Headers.Add("custom-header", "5678");
        await context.Response.WriteAsync("Hello World!");
	});
}

Сервер устанавливает заголовок custom-header и отправляет его клиенту. Чтобы клиент получил этот заголовок, он передается в метод WithExposedHeaders.

Затем на стороне клиента можно получить значение этого заголовка:

var btn = document.getElementById("btn");
var result = document.getElementById("result");
var headers = document.getElementById("headers");
var request = new XMLHttpRequest();

btn.addEventListener("click", function (e) {
	request.open("GET", "https://localhost:44313/");
    request.onreadystatechange = reqReadyStateChange;
	request.send();
});

function reqReadyStateChange() {
	if (request.readyState == 4) {
		if (request.status == 200){
			result.innerText = request.responseText;
			// получаем заголовок
			headers.innerText = request.getResponseHeader("custom-header");
		}
	}
}

Передача идентификационных данных

По умолчанию браузер не посылает никаких идентификационных данных. Подобные данные включают куки, а также данные HTTP-аутентификации. Для отправки идентификационных данных в кроссдоменном запросе на стороне клиента у объекта XMLHttpRequest необходимо установить свойство withCredentials равным true.

var request = new XMLHttpRequest();
request.open("GET", "https://localhost:44313/");
request.withCredentials = true;

Для получения данных на стороне сервера применяется метод AllowCredentials(). Этот метод устанавливает заголовок Access-Control-Allow-Credentials, который говорит браузеру, что сервер разрешает отправку идентификационных данных. При этом данный метод не может использоваться с методом AllowAllOrigin, то есть обязательно нужно указать набор адресов, с которыми будет взаимодействовать сервер. Например:

public void Configure(IApplicationBuilder app)
{
	app.UseCors(builder => builder.WithOrigins("https://localhost:44321")
								.AllowCredentials());
	app.Run(async (context) =>
	{
		var login = context.Request.Cookies["login"];		// получаем отправленные куки
		await context.Response.WriteAsync($"Hello {login}!");
	});
}

На стороне клиента отправка куки могла бы выглядеть следующим образом:

var btn = document.getElementById("btn");

var request = new XMLHttpRequest();
document.cookie = "login=tom32;";	// куки, которые будут отправляться

btn.addEventListener("click", function (e) {
	request.open("GET", "https://localhost:44313/");
	request.onreadystatechange = reqReadyStateChange;
	request.withCredentials = true;
	request.send();
});
function reqReadyStateChange() {
	if (request.readyState == 4) {
		if (request.status == 200)
			console.log(request.responseText);
	}
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850