Как решить проблему ASP.NET проблема с веб-API CORS Preflight при использовании запросов PUT и DELETE с несколькими источниками?


У меня есть ASP.NET веб-API, который вызывается тремя различными SPA. Я использую проверку подлинности windows для веб-API. Сначала я попытался настроить CORS в Интернете.конфигурация такая:

<httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="http://localhost:63342" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
        <add name="Access-Control-Allow-Credentials" value="true" />
    </customHeaders>
</httpProtocol>

Это вызвало эту предполетную проблему:

Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin (...) is therefore not allowed access.

Что я решил, добавив следующий метод в Global.асакс.cs:

protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
    {
        Response.Flush();
    }
}

Этот подход идеально работал для одного СПА. Я думал, что смогу выйти в Сеть.config и добавить другие источники, как это:

<add name="Access-Control-Allow-Origin" value="http://localhost:63342,http://localhost:63347,http://localhost:63345/>
Но, по-видимому, это не допускается. Это привело к следующей ошибке:
The 'Access-Control-Allow-Origin' header contains multiple values (...), but only one is allowed. Origin (...) is therefore not allowed access.
Поэтому, чтобы попытаться исправить это, я изменил свой подход и вместо этого решил попробовать настроить CORS на WebAPIConfig.cs, в регистре метод выглядит следующим образом:
var cors = new EnableCorsAttribute("http://localhost:63342,http://localhost:63347,http://localhost:63345", "Origin, X-Requested-With, Content-Type, Accept", "GET, POST, PUT, DELETE");
cors.SupportsCredentials = true;
config.EnableCors(cors);

Я думал, что это сработает, но теперь у меня снова ошибка перед полетом при использовании запросов PUT и DELETE, и я не знаю, как это исправить. Я отладил метод Application_BeginRequest, и он все еще топится параметры запроса, так что я понятия не имею, что вызывает эту ошибку. Кто-нибудь знает, как я могу решить эту проблему?

Правка:

Печать предполетной ошибки:

Введите описание изображения здесь

2 7

2 ответа:

Я смог решить свою проблему, дополнительно настроив метод Application_BeginRequest в Global.асакс.cs, например:

protected void Application_BeginRequest()
{
    if (Request.HttpMethod == "OPTIONS")
    {
        Response.StatusCode = (int)HttpStatusCode.OK;
        Response.AppendHeader("Access-Control-Allow-Origin", Request.Headers.GetValues("Origin")[0]);
        Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        Response.AppendHeader("Access-Control-Allow-Credentials", "true");
        Response.End();
    }
}

Этот код добавляет отсутствующие заголовки в ответ OPTIONS (запрос предварительной подготовки), которые вызвали ошибку предварительной подготовки. Поскольку у меня есть разные источники, вызывающие мой веб-API, я использую Request.Headers.GetValues("Origin")[0]) для динамического задания источника в ответе.

В WebApiConfig.cs я по-прежнему указывал различные источники, но использовал подстановочные знаки в заголовках и методы, а также установка SupportsCredentials в true, например:

var cors = new EnableCorsAttribute("http://localhost:63342,http://localhost:63347,http://localhost:63345", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);

Кроме того, если вы используете AngularJS, как я, вы должны настроить $http для использования учетных данных. Это можно настроить глобально следующим образом:

angular
.module('Application')
.config(['$httpProvider',
    function config($httpProvider) {
        $httpProvider.defaults.withCredentials = true;
    }
]);

И это все. Это решило мою проблему. Если у кого-то еще все еще есть проблемы, я рекомендую прочитать следующие публикации, которые помогли мне достичь моего ответ:

Создайте пользовательский атрибут, используя ICorsPolicyProvider что-то вроде следующего, чтобы проверить, разрешен ли запрошенный источник или нет

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false)]
    public class EnableCorsForAPIKeysAttribute :
      Attribute, ICorsPolicyProvider, IFilter
    {
        public async Task<CorsPolicy> GetCorsPolicyAsync(
          HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var corsRequestContext = request.GetCorsRequestContext();
            var originRequested = corsRequestContext.Origin;
            if (await IsValidOrigin(originRequested)) //Check if requested origin is valid or not
            {
                // Grant CORS request
                var policy = new CorsPolicy
                {
                    AllowAnyHeader = true,
                    AllowAnyMethod = true
                };
                policy.Origins.Add(originRequested);
                return policy;
            }
            else
            {
                // Reject CORS request
                return null;
            }
        }

        public bool AllowMultiple { get {return false;} }
    }

Чтобы использовать его, добавьте его в свой API-контроллер

[EnableCorsForAPIKeys]
public class APIBaseController : ApiController
{
}