Включить заголовок OPTIONS для CORS в .NET Core Web API

Я решил эту проблему после того, как не нашел решения в Stackoverflow, поэтому делюсь своей проблемой здесь и решением в ответе.

После включения междоменной политики в моем приложении .NET Core Web Api с помощью AddCors оно по-прежнему не работает в браузерах. Это связано с тем, что браузеры, включая Chrome и Firefox, сначала отправляют запрос OPTIONS, а мое приложение просто отвечает 204 No Content.


person Niels Brinch    schedule 13.02.2017    source источник
comment
Каков конкретный сценарий, при котором это не удается? Если он все время терпит неудачу для любого браузера chrome / ff, выполняющего CORS, то как это еще не покрывается фреймворком? Похоже, это было бы большим упущением.   -  person ssmith    schedule 14.02.2017
comment
Я согласен. Однако так оно и есть. Фреймворк позволит вам выполнять CORS со встроенными функциями, но он не обрабатывает вызовы OPTIONS, и это требование для нормального использования междоменных вызовов api из браузеров. Однако вы можете избежать этого, сделав более простой вызов, например, установив для типа text / plain и многое другое. Тогда браузер не будет сначала выполнять вызов OPTIONS.   -  person Niels Brinch    schedule 15.02.2017
comment
IIS должен быть тем, кто занимается этим, поэтому любой, кто читает это после ноября 2017 года, должен использовать модуль IIS CORS, blogs.iis.net/iisteam/introduction-iis-cors-1-0   -  person Lex Li    schedule 08.04.2019
comment
Да, или в службе приложений Azure, кажется, в последние несколько лет было достаточно настроить CORS в Azure и разрешить определенные домены или все домены (*). Вышеупомянутое будет работать в Azure, если в CORS не настроен домен NO, что, по-видимому, означает, что Azure позволяет приложению обрабатывать CORS самостоятельно.   -  person Niels Brinch    schedule 05.01.2021


Ответы (9)


Добавьте в проект класс промежуточного программного обеспечения для обработки команды OPTIONS.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Hosting;

namespace Web.Middlewares
{
    public class OptionsMiddleware
    {
        private readonly RequestDelegate _next;

        public OptionsMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public Task Invoke(HttpContext context)
        {
            return BeginInvoke(context);
        }

        private Task BeginInvoke(HttpContext context)
        {
            if (context.Request.Method == "OPTIONS")
            {
                context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { (string)context.Request.Headers["Origin"] });
                context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept" });
                context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET, POST, PUT, DELETE, OPTIONS" });
                context.Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
                context.Response.StatusCode = 200;
                return context.Response.WriteAsync("OK");
            }

            return _next.Invoke(context);
        }
    }

    public static class OptionsMiddlewareExtensions
    {
        public static IApplicationBuilder UseOptions(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<OptionsMiddleware>();
        }
    }
}

Затем добавьте app.UseOptions(); это как первую строку в Startup.cs в методе Configure.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseOptions();
}
person Niels Brinch    schedule 13.02.2017
comment
Я сделал именно это и могу получить запрос на попадание в промежуточное ПО, но он возвращает Запрошенный URL-адрес не может быть достигнут. Служба может быть временно отключена или может быть навсегда перемещена на новый веб-адрес. [ошибка хромированного сокета]: соединение было сброшено (соответствует ошибке TCP RST). Что я делаю не так? - person crackedcornjimmy; 13.06.2017
comment
Я не знаю. Для устранения неполадок я открывал Fiddler и проверял детали запроса и ответа. - person Niels Brinch; 19.06.2017
comment
+1, но нужно изменить одну вещь: не вызывайте _next.Invoke, если метод - это параметры, запрос должен завершиться после вызова context.Response.WriteAsync("OK");, поэтому измените реализацию Invoke на: if (context.Request.Method != "OPTIONS") { await this._next.Invoke(context); } - person Amen Ayach; 07.07.2017
comment
Это небезопасно. Почему бы вам вместо этого не настроить CORS правильно? - person Der_Meister; 23.08.2018
comment
@Der_Meister Пожалуйста, научите меня. В моем случае я хочу, чтобы мой API был доступен с любого веб-сайта. - person Niels Brinch; 23.08.2018
comment
Вы можете использовать CorsPolicyBuilder.AllowAnyOrigin (). В вашем ответе нет никаких оговорок, кто-то может найти его и применить, не понимая. - person Der_Meister; 24.08.2018
comment
Это блестяще сработало для нашего внешнего интерфейса Angular 6 с серверной частью .NET Core. Это вместе с AddCors, которое позволяет следующее: AllowAnyOrigin, AllowAnyMethod, AllowAnyHeader и AllowCredentials работает нормально. Наш API теперь полностью общедоступен. Нам пришлось добавить заголовок X-Auth в промежуточное ПО OPTIONS, но это потому, что мы используем X-Auth от нашего клиента к нашему бэкэнду. - person MortenMoulder; 18.10.2018
comment
Просто использовал это в моем приложении, но он не работает. Я что-то делаю неправильно? Вот ссылка на мой вопрос на stackoverflow.com/questions/53479946/ - person Mcbaloo; 26.11.2018
comment
После нескольких месяцев этого ответа Microsoft дала официальное решение, blogs.iis. net / iisteam / introduction-iis-cors-1-0 - person Lex Li; 08.04.2019
comment
@AmenAyach не имеет значения, потому что внутри оператора if он возвращает context.Response.WriteAsync("OK");, поэтому _next.invoke(context) никогда не достигается, если метод OPTIONS - person Jesse de gans; 18.12.2019

Я знаю, что на него был дан ответ. Просто отвечаю с обновленной информацией. Так что это поможет другим.

Теперь он встроен в платформу ASP.NET Core.

Просто следуйте https://docs.microsoft.com/en-us/aspnet/core/security/cors

и заменить

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

с участием

        app.UseCors(builder =>
       builder.WithOrigins("http://example.com")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials());
person Jeyara    schedule 04.03.2018
comment
Это работает? Проблема заключалась в том, что Chrome сначала отправляет вызов OPTIONS и не получает разрешения, поэтому настоящий вызов никогда не поступает из браузера Chrome. Это то, что решает AllowAnyMethod? Изменить: теперь я вижу, что это именно то, о чем они говорили в статье, на которую вы ссылаетесь! - person Niels Brinch; 21.03.2018
comment
к сведению: если вы не хотите разрешать какие-либо заголовки или методы, вы можете использовать методы _1 _ / _ 2_ - person Griddo; 07.06.2018
comment
при этом я получаю 204 No Content в коде статуса ответа - person Nitin S; 29.01.2019
comment
Я также получаю 204 No Content ответ при попытке этого - person Mark Entingh; 16.04.2019
comment
204 No Content проблема? Похоже, единственная информация, которую вы должны ожидать от OPTIONS запроса, находится в заголовках. Нет? Mozilla, похоже, согласна. - person Fing Lixon; 25.07.2019
comment
Это должно войти в Configure() в Startup.cs - person Luke; 22.11.2019

Это сработало для меня:

Убедитесь, что это:

app.UseCors(builder => {
    builder.AllowAnyOrigin();
    builder.AllowAnyMethod();
    builder.AllowAnyHeader();
});

Происходит перед любым из них:

app.UseHttpsRedirection();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseCookiePolicy();

Помните, мы имеем дело с «конвейером». Корс должен быть первым.

-гимзани

person user1628627    schedule 19.04.2019
comment
Вопрос в том, применяются ли эти настройки при вызове OPTIONS. - person Niels Brinch; 26.04.2019
comment
Спасибо, у меня это сработало. Я хотел бы подчеркнуть, что материал cors должен быть первым, прежде чем любой другой метод конфигурации конвейера HTTP-запросов, как сказал @NielsBrinch. - person Acemond; 24.02.2020
comment
Интересно, почему от IDE или сервера нет предупреждений о подобных ошибках. Это простые ловушки, в которых могут оказаться ничего не подозревающие разработчики, которые потратят дополнительные часы на отладку. Я предполагаю, что stackoverflow помог обуздать МНОГО депрессивного состояния разработчиков. - person Prince Owen; 07.04.2020

Нет необходимости в дополнительном промежуточном программном обеспечении. Как уже упоминалось выше, единственное, что необходимо, - это метод OPTIONS, разрешенный в конфигурации Cors. Вы можете AllowAnyMethod, как предлагается здесь: https://stackoverflow.com/a/55764660/11921910

Но безопаснее просто разрешить определенные вещи вроде этого:

app.UseCors(builder => builder
.WithOrigins("https://localhost", "https://production.company.com") /* list of environments that will access this api */
.WithMethods("GET", "OPTIONS") /* assuming your endpoint only supports GET */
.WithHeaders("Origin", "Authorization") /* headers apart of safe-list ones that you use */
);

Некоторые заголовки разрешены всегда: https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header

person Taras Zavorotnii    schedule 13.08.2019

Проблема с параметрами AspNetCoreModuleV2

Основной модуль .Net не знает, как обрабатывать OPTIONS, что вызывает проблему CORS перед полетом, поэтому решение состоит в том, чтобы исключить команду OPTIONS из обработки им. Это делается заменой * на нужные вам глаголы, кроме OPTIONS. Не волнуйтесь, команда OPTIONS будет обрабатываться загруженным по умолчанию OPTIONSHandler:

IIS

Решение: изменить web.config

 <add name="aspNetCore" path="*" verb="* modules="AspNetCoreModuleV2" resourceType="Unspecified" />

Сделайте это так:

<add name="aspNetCore" path="*" verb="GET,POST,PUT,DELETE" modules="AspNetCoreModuleV2" resourceType="Unspecified" />

IIS Express: для отладчика Visual Studio

Я попытался изменить .vs\ProjectName\config\applicationhost.config внизу файла, но безнадежно. Таким образом, в данном конкретном случае вы можете использовать выбранный ответ.

person Shadi Namrouti    schedule 04.01.2021

Я хотел разрешить это для одного метода, а не использовать промежуточное ПО, чтобы разрешить это для любого метода. Вот чем я закончил:

Ручная обработка метода OPTIONS

[HttpOptions("/find")]
public IActionResult FindOptions()
{
    Response.Headers.Add("Access-Control-Allow-Origin", new[] { (string)Request.Headers["Origin"] });
    Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept" });
    Response.Headers.Add("Access-Control-Allow-Methods", new[] { "POST, OPTIONS" }); // new[] { "GET, POST, PUT, DELETE, OPTIONS" }
    Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
    return NoContent();
}

[HttpPost("/find")]
public async Task<IActionResult> FindOptions([FromForm]Find_POSTModel model)
{
    AllowCrossOrigin();
    
    // your code...
}

private void AllowCrossOrigin()
{
    Uri origin = null;
    Uri.TryCreate(Request.Headers["Origin"].FirstOrDefault(), UriKind.Absolute, out origin);

    if (origin != null && IsOriginAllowed(origin))
        Response.Headers.Add("Access-Control-Allow-Origin", $"{origin.Scheme}://{origin.Host}");
}

И, конечно, вы можете реализовать IsOriginAllowed по своему желанию.

private bool IsOriginAllowed(Uri origin)
{
    const string myDomain = "mydomain.com";
    const string[] allowedDomains = new []{ "example.com", "sub.example.com" };

    return 
           allowedDomains.Contains(origin.Host) 
           || origin.Host.EndsWith($".{myDomain}");
}

Вы можете найти более подробную информацию на как включить CORS для запросов POST на одной конечной точке

person Jean    schedule 10.06.2019

Я хочу дать конкретный ответ для моей конкретной ситуации, когда я тестировал как api, так и клиентское веб-приложение локально. Я знаю, что это поздно, но CORS так сильно изменился в ядре dot net, я подумал, что такие новички, как я, могли бы извлечь выгоду из полного сообщения.

Для меня это были две проблемы, которые возникли один за другим.

  1. Ошибка отклонения CORS
  2. а также проблема OPTIONS в firefox (я предполагаю, что хром будет делать то же самое)
  3. также мой API работает с HTTPS
  4. веб-приложение без HTTPS
  5. оба они работают локально, снова упомянув об этом для ясности.

Во-первых, это общедоступный void ConfigureServices (сервисы IServiceCollection)

        //lets add some CORS stuff 
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder => {
                builder.WithOrigins("http://localhost:3000",
                                    "http://www.contoso.com");
                builder.AllowAnyMethod();
                builder.AllowAnyHeader();
                builder.AllowCredentials();
            });
        });

а затем это переходит в public void Configure (приложение IApplicationBuilder, IWebHostEnvironment env)

  app.UseCors();
person Jay    schedule 30.07.2020

Добро пожаловать .

[HttpOptions (/ find)] общедоступный IActionResult FindOptions ()

{
    Response.Headers.Add("Access-Control-Allow-Origin", new[] { (string)Request.Headers["Origin"] });
    Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Origin, X-Requested-With, Content-Type, Accept" });
    Response.Headers.Add("Access-Control-Allow-Methods", new[] { "POST, OPTIONS" }); // new[] { "GET, POST, PUT, DELETE, OPTIONS" }
    Response.Headers.Add("Access-Control-Allow-Credentials", new[] { "true" });
    return NoContent();
}`enter code here`
person Tyler Tx Roofing Pro    schedule 30.07.2020
comment
Этот вопрос уже содержит несколько ответов и принятый ответ. Вы можете объяснить, чем ваш ответ отличается от других ответов? Также знайте, что ответы, содержащие только код, бесполезны в долгосрочной перспективе. - person 7uc1f3r; 30.07.2020

На самом деле, ни один из ответов у меня не сработал, но я наконец понял, в чем проблема, и не могу поверить, что просто переместил app.UserCors("PolicyName"); перед app.UseAuthorization();, и он начал работать!

Я подумал, что это может быть кому-то полезно.

services.AddCors(options =>
{
  options.AddPolicy("EnableCORS", bl =>
  {
    bl.WithOrigins(origins)
      .AllowAnyMethod()
      .AllowAnyHeader()
      .AllowCredentials()
      .Build();
  });
});


..........................
app.UseAuthentication();
app.UseCors("EnableCORS");
.....
app.UseAuthorization();
person ramin azadi    schedule 03.03.2021