Одной из самых неочевидных проблем, с которыми может столкнуться начинающий разработчик ASP.NET Core является CORS.
CORS (Cross-origin resource sharing — «совместное использование ресурсов между разными источниками») — это технология современных браузеров, которая позволяет предоставить веб-страницам доступ к ресурсам другого домена. По умолчанию веб-браузеры в целях безопасности ограничивают ajax-запросы между различными доменами и такое поведение иногда может оказаться неприемлемым для наших приложений ASP.NET Core. В этом случае мы должны использовать возможности платформы ASP.NET Core для настройки CORS в своем приложении.
Демонстрация CORS
Чтобы понять в чем заключается проблема (она же смысл CORS) рассмотрим такой вполне вероятный сценарий развития нашего проекта: вы разрабатываете прекрасный Web API и хотите, чтобы множество пользователей его использовали, разработав собственные клиенты. Один из пользователей API решает разработать своё веб-приложение, в котором выполняется следующий ajaх-запрос
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>API Client</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("http://localhost:5000/weatherforecast"); if (response.ok) result.innerText = await response.text(); } catch (e) { result.innerText = e.message; } }); </script> </body> </html>
То есть пользователь пытается вывести на экран JSON-представление прогноза погоды или текст возникшей ошибки. Точно такой же запрос мы выполняли уже сотни раз, используя http-файл проекта, и он прекрасно работает. Попробуем открыть страницу с ajax-запросом из листинга выше в браузере и выполнить запрос, предварительно запустив наше приложение.
Вопреки нашим ожиданиям получить объект задачи, в браузере мы увидим вот такую ошибку
В консоли разработчика мы можем получить более подробную информацию по ошибке
Перед непосредственным запросом ресурса современные браузеры отправляют на сервер так называемый предполетный запрос (preflight request), который проверяет, понятен ли протокол CORS и осведомлен ли сервер об использовании определенных методов и заголовков. Для нас, как разработчиков API, это обычный запрос с использование метода OPTIONS
. В нашем случае браузер ожидал получить в ответе сервера заголовок Access-Control-Allow-Origin
, но так как так такого заголовка в ответе не оказалось, то браузер выдал клиенту ошибку CORS.
Итак, мы столкнулись с ошибкой CORS. Чтобы её избежать, мы должны настроить CORS в своем приложении. Вообще, тема использования CORS в приложениях ASP.NET Core довольно обширная и затрагивает множество аспектов использования CORS. Мы же в этой части ограничимся тем минимумом информации, который будет достаточен для того, чтобы к вашему приложению могли обращаться самые различные клиенты в Сети, не боясь получить ошибку CORS.
Настройка CORS
Итак, вернемся к нашему приложению. Для того, чтобы клиент, используя браузер, мог выполнять запросы к нашему API, мы должны включить поддержку CORS в приложении. Перейдем в файл Program.cs и добавим в него следующие строки
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddCors();//добавляем сервис для CORS var app = builder.Build(); //настраиваем CORS app.UseCors(config => { config.AllowAnyOrigin(); }); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
Здесь мы добавляем новый сервис для работы с CORS, используя настройки сервиса по умолчанию
builder.Services.AddCors();
а также добавляем в конвейер обработки запросов и настраиваем компонент middleware – CorsMiddleware
:
app.UseCors(config=> { config.AllowAnyOrigin(); });
Для настройки CORS мы используем делегат вида Action<CorsPolicyBuilder>
. Класс CorsPolicyBuilder
позволяет настроить политику CORS, используя различные методы
Метод | Описание |
AllowAnyHeader() |
Принимаются запросы с любыми заголовками |
AllowAnyMethod() |
Принимаются любые HTTP-методы(GET/POST/PUT и т.д.) |
AllowAnyOrigin() |
Принимаются запросы от любых источников (с любого хоста/адреса) |
AllowCredentials() |
Допускается принимать учётные данные от пользователей, например аутентификационные куки |
DisallowCredentials() |
Запрещает принимать учётные данные от пользователей |
WithHeaders(String[]) |
Принимаются только те запросы, которые содержат определенные заголовки |
WithMethods(String[]) |
Принимаются только указанные HTTP-методы |
WithOrigins(String[]) |
Запросы принимаются только от указанных источников |
Таким образом, в нашем приложении мы разрешили принимать запросы от любых источников и теперь браузер должен отработать запрос без ошибок. Снова запустим приложение и попытаемся в браузере выполнить запрос. Результат выполнения запроса в Яндекс.Браузере представлен на рисунке ниже.
Как можно видеть по рисунку, сервер вернул клиенту в том числе и заголовок Access-Control-Allow-Origin
, а браузер вернул данные без ошибки CORS.
Представленный пример – это минимум того, что необходимо сделать для того, чтобы клиенты могли работать с нашим API. При необходимости мы можем использовать и другие методы CorsPolicyBuilder
, например, разрешить принимать запросы с определенными методами HTTP и только с указанными заголовками
app.UseCors(config=> { config.AllowAnyOrigin() .WithMethods("PUT") .WithHeaders("X-Version"); });
Политики CORS
При необходимости мы можем определять в своем приложении различные политики CORS, используя их для конкретных действий и контроллеров. Политики CORS создаются с помощью делегата Action<CorsOptions>
, который передается непосредственно в метод расширения AddCors()
.
Класс CorsOptions
содержит следующие методы для работы с политиками CORS
Метод | Описание |
AddDefaultPolicy(Action<CorsPolicyBuilder>) |
Добавляет новую политику и задает ее в качестве политики по умолчанию. |
AddDefaultPolicy(CorsPolicy) |
Добавляет новую политику и задает ее в качестве политики по умолчанию |
AddPolicy(String, Action<CorsPolicyBuilder>) |
Добавляет новую именованную политику |
AddPolicy(String, CorsPolicy) |
Добавляет новую именованную политику |
GetPolicy(String) |
Возвращает объект именованной политики |
Как можно видеть по данным таблицы, при создании политики CORS мы можем использовать всё тот же объект CorsPolicyBuilder
и его методы. Например, перенесем настройки CORS нашего приложения в отдельную политику, а также создадим новую, используя CorsOptions
builder.Services.AddCors(config => { config.AddPolicy("FirstPolicy", policy => { policy.WithMethods(["GET"]); policy.WithHeaders(["X-Version"]); policy.AllowAnyOrigin(); }); config.AddPolicy("SecondPolicy", policy => { policy.WithMethods(["DELETE"]); policy.AllowAnyOrigin(); }); });
Здесь мы добавили две политики CORS с именами «FirstPolicy» и «SecondPolicy». Теперь мы можем использовать эти политики в своем приложении. Для того, чтобы воспользоваться политикой CORS в контроллере, используется атрибут [EnableCors]
. Используем наши политику в контроллере
[HttpGet] [EnableCors("FirstPolicy")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); }
Чтобы отключить CORS для определенного действия ли контроллера – используется атрибут [DisableCors]
, например,
[DisableCors] public async Task<ActionResult> Get([FromQuery] TasksParameters parameters)
Таким образом, используя различные политики CORS и атрибуты [EnableCors]
и [DisableCors]
мы можем производить более тонкую настройку CORS в нашем приложении применительно к отдельным контроллерам и их действиям.
Итого
CORS (Cross-origin resource sharing — «совместное использование ресурсов между разными источниками») — это технология современных браузеров, которая позволяет предоставить веб-страницам доступ к ресурсам другого домена. По умолчанию веб-браузеры в целях безопасности ограничивают ajax-запросы между различными доменами. Для настройки CORS в нашем приложении ASP.NET Core Web API необходимо подключить сервис для работы с CORS, а также подключить в конвейер обработки запросов новый компонент middleware для работы CORS.