Использование CORS в ASP.NET Core

По умолчанию браузеры отклоняют AJAX-запросы к адресам, расположенных в других доменах. Но иногда такие запросы необходимы. Для того, чтобы это регулировать придумали CORS.

Cross Origin Resource Sharing (CORS) — стандарт W3C, который позволяет определить политики кросс-доменных AJAX-запросов. Подробнее про CORS можно почитать здесь и здесь.

Как видно из спецификаций, управление политиками CORS происходит за счет дополнительных HTTP-запросов и HTTP-заголовков при осуществлении кросс-доменных запросов. В ASP.NET Core существует готовый пакет для настройки политик CORS, что упрощает работу с кросс-доменными запросами.

Установка пакета

В первую очередь следует установить пакет Microsoft.AspNetCore.Cors — в нем находится все необходимое для настройки CORS.

После установки пакета следует зарегистрировать сервис CORS в контейнере ASP.NET:

public void ConfigureServices(IServiceCollection services)
{
  services.AddCors();
}

Настройка источника

Кроме добавления CORS в DI-контейнер, следует указать конвейеру ASP.NET Core о необходимости использования Middleware из пакета Microsoft.AspNetCore.Cors. Сделать это можно вызвав метод UseCors(). В качестве параметра передается выражение, настраивающее политику CORS. Зададим домены, которым можно работать с нашим ресурсом в рамках CORS:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  app.UseCors(builder =>
    builder.WithOrigins("https://blog.zwezdin.com/")
  );
}

Параметр builder — это объект CorsPolicyBuilder. С его помощью можно указать как именно должен работать CORS для нашего ресурса. Вся дальнейшая настройка CORS производится с его помощью.

Указание HTTP-методов и заголовков

Если вам требуется более тонкая настройка CORS, то следует воспользоваться тем же CorsPolicyBuilder, но задать больше параметров. Например, если в рамках CORS-запросов вы захотите передавать HTTP-заголовки, отличные от стандартных, то такой запрос будет отклонен. Следующая конфигурация позволит работать с нестандартными заголовками, а также указать HTTP-методы, разрешенные для работы и набор хостов-источников:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  app.UseCors(builder =>
    builder.WithOrigins("https://blog.zwezdin.com/",
      "https://google.com")
      .WithHeaders("X-Header1", "X-Header1")
      .WithMethods("GET", "POST", "PUT")
  );
}

Можно также отменить ограничительную политику и разрешить всё:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  app.UseCors(builder =>
    builder.AllowAnyOrigin()
      .AllowAnyHeader()
      .AllowAnyMethod()
  );
}

Пользоваться, однако, такой методикой я бы посоветовал с осторожностью.

Определение политик

Если предполагается, что отдельные части приложения должны иметь разное поведение для CORS-запросов, то можно воспользоваться политиками. Политики — это именованый набор правил CORS, аналогичный тому, как это описывалось выше. Описание политик производится в методе AddCors() при добавлении сервиса в DI-контейнер:

public void ConfigureServices(IServiceCollection services)
{
  services.AddCors(cors =>
  {
    cors.AddPolicy("Policy1",
      builder =>
        builder.WithOrigins("https://blog.zwezdin.com/")
          .WithHeaders("X-Header1", "X-Header1")
          .WithMethods("GET", "POST", "PUT"));

    cors.AddPolicy("Policy2",
      builder =>
        builder.AllowAnyOrigin()
          .WithMethods("GET"));

    cors.AddPolicy("Policy3",
      builder =>
        builder.AllowAnyOrigin()
          .WithHeaders("X-Header1")
          .WithMethods("POST"));
  });
}

Здесь определяется три политики с именами Policy1, Policy2 и Policy3. Далее есть несколько способов как эти политики можно применять.

Можно указать имя политики вместо явной конфигурации при добавлении Middleware.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  app.UseCors("Policy2");
}

Такой подход позволяет иметь несколько предопределенных политик и переключать их, например, используя конфигурационный файл.

Если в приложении используется ASP.NET MVC, то политики CORS можно применять к отдельным контроллерам или действиям:

[EnableCors("Policy2")]
public class HomeController : Controller
{
  [EnableCors("Policy1")]
  public IActionResult Index()
  {
    return View();
  }

  [EnableCors("Policy2")]
  public IActionResult About()
  {
    return View();
  }
}

Если добавить фильтр CorsAuthorizationFilterFactory глобально, то заданная политика будет применяться ко всем контроллерам/действиям. При этом можно явно указать на какие действия эта политика распространяться не должна:

[EnableCors("Policy2")]
public class HomeController : Controller
{
  [EnableCors("Policy1")]
  public IActionResult Index()
  {
    return View();
  }

  [EnableCors("Policy2")]
  public IActionResult About()
  {
    return View();
  }

  [DisableCors]
  public IActionResult Contact()
  {
    return View();
  }
}