54

ASP.NETMVCコントローラーアクションに対してクロスドメインPOST要求を実行しようとしています。このコントローラーアクションは、さまざまなパラメーターを受け入れて使用します。問題は、プリフライトリクエストが発生すると、コントローラーアクションが実際に実行を試み、OPTIONSリクエストがデータを渡さないため、コントローラーアクションが500HTTPエラーをスローすることです。パラメータを使用するコードまたはパラメータ自体を削除すると、リクエストチェーン全体が正常に完了します。

これがどのように実装されるかの例:

コントローラのアクション

public ActionResult GetData(string data)
{
    return new JsonResult
    {
        Data = data.ToUpper(),
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
}

クライアント側のコード

<script type="text/javascript">
        $(function () {
            $("#button-request").click(function () {
                var ajaxConfig = {
                    dataType: "json",
                    url: "http://localhost:8100/host/getdata",
                    contentType: 'application/json',
                    data: JSON.stringify({ data: "A string of data" }),
                    type: "POST",
                    success: function (result) {
                        alert(result);
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        alert('Error: Status: ' + textStatus + ', Message: ' + errorThrown);
                    }
                };

                $.ajax(ajaxConfig);
            });
        });
    </script>

これで、プリフライトリクエストが発生するたびに、500 HTTPコードが返されます。これは、「data」パラメータがnullであるため、OPTIONSリクエストが値を渡さないためです。

サーバーアプリケーションは、ローカルIISのポート8100にセットアップされており、クライアント側のコードを実行するページは、クロスドメイン呼び出しを模倣するようにポート8200にセットアップされています。

また、次のヘッダーを使用してホスト(8100上)を構成しました。

Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Origin: http://localhost:8200

私が見つけた回避策の1つは、アクションを実行するHTTPメソッドをチェックし、それがOPTIONSリクエストの場合は空白のコンテンツを返すだけで、それ以外の場合はアクションコードを実行することでした。そのようです:

public ActionResult GetData(string data)
{
    if (Request.HttpMethod == "OPTIONS") {
        return new ContentResult();
    } else {
        return new JsonResult
        {
            Data = data.ToUpper(),
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
    }
}

しかし、このアプローチは私には非常に不格好に感じます。この種のロジックをに追加することを検討しましたAttributeが、これでも、CORSを使用して呼び出されるすべてのアクションを装飾することを意味します。

この機能を機能させるためのより洗練されたソリューションはありますか?

4

6 に答える 6

67

だから私はうまくいく解決策を見つけました。リクエストごとに、それがCORSリクエストであるかどうか、およびリクエストがOPTIONS動詞で受信されているかどうかを確認し、プリフライトリクエストであることを示します。そうである場合は、空の応答(もちろん、IISで構成されたヘッダーのみが含まれています)を返送するだけなので、コントローラーアクションの実行は無効になります。

次に、クライアントがプリフライトから返されたヘッダーに基づいてリクエストの実行が許可されていることを確認すると、実際のP​​OSTが実行され、コントローラーアクションが実行されます。そして私のコードの例:

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

前述のように、これは私にとってはうまくいきましたが、誰かがより良い方法、または私の現在の実装の欠陥を知っているなら、私はそれらについて聞いていただければ幸いです。

于 2012-11-30T13:24:50.583 に答える
12

カールの答えを拡張して、私は彼のコードを取得し、それを私のOWINパイプラインに接続しました。

app.Use((context, next) =>
{
     if (context.Request.Headers.Any(k => k.Key.Contains("Origin")) && context.Request.Method == "OPTIONS")
     {
         context.Response.StatusCode = 200;
         return context.Response.WriteAsync("handled");
     }

     return next.Invoke();
});

これをStartup.csのIAppBuilderの先頭(またはWebAPIを登録する前の任意の場所)に追加するだけです。

于 2015-07-31T19:46:01.840 に答える
7

受け入れられた答えは魅力のように機能しますが、要求が実際にコントローラーに渡されていることがわかりました。ステータスコードを受け取ってい200ましたが、応答の本文には、コントローラーからの例外を除いて、多くのHTMLが含まれていました。したがって、を使用する代わりに、リクエストの実行を停止するResponse.Flush()を使用する方がよいことがわかりました。Response.End()この代替ソリューションは次のようになります。

編集:元の回答からのタイプミスを修正しました。

protected void Application_BeginRequest()
{
    if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) &&
        Request.HttpMethod == "OPTIONS") {
        Response.End();
    }
}
于 2017-05-25T20:14:00.280 に答える
6

ASP.NetWebApiでプリフライト/CORSの問題を処理した方法は次のとおりです。Microsoft.AspNet.WebApi.Cors NugetパッケージをWebプロジェクトに追加しただけです。次に、WebApiConfig.csファイルに次の行を追加しました。

config.EnableCors(new ApplicationCorsPolicy());

カスタムPolicyProviderクラスを作成しました

public class ApplicationCorsPolicy : Attribute, ICorsPolicyProvider
{
    public async Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var corsRequestContext = request.GetCorsRequestContext();
        var originRequested = corsRequestContext.Origin;

        if (await IsOriginFromAPaidCustomer(originRequested))
        {
            // Grant CORS request
            var policy = new CorsPolicy
            {
                AllowAnyHeader = true,
                AllowAnyMethod = true
            };
            policy.Origins.Add(originRequested);
            return policy;
        }
        // Reject CORS request
        return null;
    }

    private async Task<bool> IsOriginFromAPaidCustomer(string originRequested)
    {
        // Do database look up here to determine if origin should be allowed.
        // In my application I have a table that has a list of domains that are
        // allowed to make API requests to my service. This is validated here.
        return true;
    }
}

参照してください。Corsフレームワークを使用すると、許可されるオリジンなどを決定するための独自のロジックを追加できます。これは、REST APIを外部に公開し、サイトにアクセスできるユーザー(オリジン)のリストがデータベースのような制御された環境で。さて、単にすべてのオリジンを許可している場合(すべての場合にそれほど良い考えではないかもしれません)、WebApiConfig.csでこれを行うだけで、CORSをグローバルに有効にできます。

config.EnableCors();

WebApiのフィルターとハンドラーと同様に、次のようにコントローラーにクラスまたはメソッドレベルのアノテーションを追加することもできます。

[EnableCors("*, *, *, *")]

EnableCors属性には、次のパラメーターを受け入れるコンストラクターがあることに注意してください

  1. 許可されたオリジンのリスト
  2. 許可されるリクエストヘッダーのリスト
  3. 許可されるHTTPメソッドのリスト
  4. 許可される応答ヘッダーのリスト

どのリソースへのアクセスを許可するかを各コントローラー/エンドポイントで静的に指定できます。

2016年6月24日更新: Web.configに次のものがあることに言及する必要があります。これらがすべての人のデフォルトではないようです。

<system.webServer>
    <handlers>
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <remove name="OPTIONSVerbHandler" />
        <remove name="TRACEVerbHandler" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
        </handlers>
</system.webServer>

出典:Microsoft

于 2016-05-05T09:37:07.950 に答える
3

これは赤いニシンかもしれません。私は最近、あなたがしているフープを飛び越えずにCORSを正常に動作させました。

これは、Thinktecture.IdentityModel nugetパッケージの組み合わせを使用して行われ、さらに重要なことに...WebDAVへのすべての参照の削除。これには、IISからwebdavモジュールを削除し、Web構成で次の行を確認することが含まれます。

<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <remove name="WebDAVModule" />
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
    </modules>
    <handlers>
      <remove name="WebDAV" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>

次に、thinktectureを使用して、次のような静的クラスを使用してGlobal.asaxからCORSを構成できます。

public class CorsConfig
{
    public static void RegisterCors(HttpConfiguration httpConfiguration)
    {
        var corsConfig = new WebApiCorsConfiguration();
        corsConfig.RegisterGlobal(httpConfiguration);

        corsConfig.ForAllResources().AllowAllOriginsAllMethodsAndAllRequestHeaders();
    }
}

出典:http ://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/

于 2012-11-30T13:36:35.537 に答える
3

これらの答えはどれも私にはうまくいきませんでしたが、次のwebconfig設定はうまくいきました。私にとっての2つの重要な設定は、 :を削除する行を設定Access-Control-Allow-HeadersContent-Typeてコメントアウトすることでした。OPTIONSVerbHandler

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"></modules>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
      </customHeaders>
    </httpProtocol>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <!--<remove name="OPTIONSVerbHandler" />-->
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
于 2016-08-01T06:57:39.613 に答える