web-dev-qa-db-ja.com

複数のオリジンでPUTおよびDELETEリクエストを使用する場合のASP.NET Web API CORSプリフライトの問題を解決するにはどうすればよいですか?

3つの異なるSPAから呼び出されるASP.NET Web APIがあります。 Web APIにWindows認証を使用しています。最初に、次のようにWeb.configで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.asax.csに次のメソッドを追加して解決しました。

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

このアプローチは、単一のSPAで完全に機能しました。 Web.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.

したがって、これを試して修正するために、私は自分のアプローチを変更し、代わりに、次のようなRegisterメソッドで、WebAPIConfig.csでCORSを構成しようとすることにしました。

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メソッドをデバッグしましたが、OPTIONSリクエストをフラッシュしているため、このエラーの原因がわかりません。この問題を解決する方法を誰かが知っていますか?

編集:

プリフライトエラーの印刷:

enter image description here

12
João Paiva

次のように、Global.asax.csのApplication_BeginRequestメソッドをさらにカスタマイズすることで問題を解決できました。

_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応答(プリフライトリクエスト)に欠落しているヘッダーを追加します。 Web 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;
    }
]);
_

以上です。これは私の問題を解決しました。他の誰かがまだ問題を抱えている場合は、次の出版物を読むことをお勧めします。

8
João Paiva

ICorsPolicyProviderを使用してカスタム属性を作成し、要求されたOriginが許可されているかどうかを確認するために次のようなものを作成します

    [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
{
}
1
Paresh