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

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

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddCors(); // добавляем сервисы CORS

var app = builder.Build();

// настраиваем CORS
app.UseCors(builder => builder.AllowAnyOrigin());

app.Map("/", async context => await context.Response.WriteAsync("Hello METANIT.COM!"));

app.Run();

В вызове 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:7027").WithMethods("GET"));

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

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

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

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

app.UseCors(builder => builder.WithOrigins("https://localhost:7027")
							.AllowAnyMethod()
							.WithHeaders("custom-header"));

В данном случае необходимо, чтобы клиент отправлял в запросе заголовок "custom-header". Например, отправка данного заголовка в коде javascript с помощью функции fetch:

<h2 id="result"></h2>
<button id="btn" value="Запрос">Запрос</button>

<script>
    const btn = document.getElementById("btn");
    const result = document.getElementById("result");
    btn.addEventListener("click", async () => {
        try {
            const response = await fetch("https://localhost:7199/", { headers: { "custom-header": "test" } });
            if (response.ok) result.innerText = await response.text();
        }
        catch (e) {
            result.innerText = e.message;
        }
    });
</script>

Получение заголовков на клиенте

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddCors(); // добавляем сервисы CORS

var app = builder.Build();

// настраиваем CORS
app.UseCors(builder => builder.WithOrigins("https://localhost:7027")
                             .AllowAnyMethod()
                             .AllowAnyHeader()
                             .WithExposedHeaders("custom-header"));

app.Run(async (context) =>
{
    context.Response.Headers.Add("custom-header", "5678");
    await context.Response.WriteAsync("Hello World!");
});

app.Run();

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

Затем на стороне клиента можно получить значение этого заголовка. Например, получение в коде JavaScript с помощью функции fetch:

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8" />
   <title>Test CORS</title>
</head>
<body>
   <h2 id="result"></h2>
   <button id="btn" value="Запрос">Запрос</button>

   <script>
        const btn = document.getElementById("btn");
        const result = document.getElementById("result");
        btn.addEventListener("click", async () => {
            try {
                const response = await fetch("https://localhost:7199/");
                if (response.ok) {
                    const headerTitle = "custom-header"; // название заголовка
                    result.innerText = await response.text();
                    if (response.headers.has(headerTitle)) {  // если заголовок есть
                        console.log(response.headers.get(headerTitle));     // получаем его значение
                    }
                }
            }
            catch (e) {
                result.innerText = e.message;
            }
        });
   </script>
</body>
</html>

Альтернативное получение через XMLHttpRequest:

const btn = document.getElementById("btn");
const result = document.getElementById("result");
const headers = document.getElementById("headers");
const 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.

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

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

var builder = WebApplication.CreateBuilder();

builder.Services.AddCors(); // добавляем сервисы CORS

var app = builder.Build();

// настраиваем CORS
app.UseCors(builder => builder.WithOrigins("https://localhost:7027")
                             .AllowCredentials());

app.Run(async (context) =>
{
    var login = context.Request.Cookies["login"];  // получаем отправленные куки
    await context.Response.WriteAsync($"Hello {login}!");
});

app.Run();

При отправке запроса с помощью функции fetch ей необходимо передать опцию credentials со значением include

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8" />
   <title>Test CORS</title>
</head>
<body>
   <h2 id="result"></h2>
   <button id="btn" value="Запрос">Запрос</button>

   <script>
        const btn = document.getElementById("btn");
        const result = document.getElementById("result");
        document.cookie = "login=tom32"; // куки, которые будут отправляться
        btn.addEventListener("click", async () => {
            try {
                const response = await fetch("https://localhost:7199/", { credentials: "include"});
                if (response.ok)  result.innerText = await response.text();
                
            }
            catch (e) {
                result.innerText = e.message;
            }
        });
   </script>
</body>
</html>

Альтернативный вариант с помощью XMLHttpRequest:

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

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

btn.addEventListener("click", function () {
	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