さまざまなドメインのWCFサービスを操作する問題を解決するための可能なアプローチの1つを示したいと思います。 このトピックで見つけた情報は不完全であるか、理解しにくい情報が多すぎたためです。 Cookieや承認に関する情報など、WCFとAJAX POSTリクエスト間の相互作用のいくつかの方法についてお話したいと思います。
ご存じのように、セキュリティ上の理由により、別のドメインへのAJAX呼び出しは機能しません。 この問題を解決するために、CORS標準( wiki 、 mozilla )が発明され、リリースされました。 この標準は、特定のHTTPヘッダーを使用してアクセスを許可および制限することを意味します。 このプロトコルを使用した簡略化された通信プロセスは、以下を意味します。
クライアント(ブラウザー)は
Origin
HTTPヘッダーとの接続を開始します。サーバーは
Access-Control-Allow-Origin
ヘッダーを使用して応答する必要があります。 アドレスからのリクエスト/レスポンスのペアの例
foo.example
サービスへの
foo.example
bar.other/resources/public-data
bar.other/resources/public-data
:
リクエスト:
GET /リソース/パブリックデータ/ HTTP / 1.1
ホスト:bar.other
起源:foo.example
[その他のヘッダー]
答えは:
HTTP / 1.1 200 OK
日付:2008年12月1日月曜日00:23:53 GMT
Access-Control-Allow-Origin:*
Content-Type:アプリケーション/ xml
[XMLデータ]
見出し
-
Access-Control-Allow-Origin
このヘッダーは、リクエストがどのリソースから来るかを決定します。*
または、特定のドメインを使用できます。foo.example
foo.example
このヘッダーは1つのみであり、1つの値のみを含むことができます。 ドメインリストは指定できません。 -
Access-Control-Allow-Methods
このヘッダーは、サーバーとの通信に使用できるメソッドを定義します。POST,GET,OPTIONS
に限定していますが、PUT
、DELETE
などを使用することもできます。 -
Access-Control-Allow-Headers
このヘッダーは、使用可能なヘッダーのリストを定義します。 たとえば、応答タイプをapplication/json
に設定できるContent-Type
。 -
Access-Control-Allow-Credentials
このヘッダーは、Cookieと認証ヘッダーを許可するかどうかを決定します。 可能な値はtrue
およびfalse
です。 重要:データは、特定のドメインがAccess-Control-Allow-Origin
ヘッダーで明示的に設定されている場合にのみ転送されます。*
が使用されている場合、ヘッダーは無視され、データは送信されません。
一般に、ブラウザは制限を課します。 ヘッダーの内容が気に入らない場合、ユーザーにこのデータを提供しません(必要な
Access-Control-Allow-Headers
が返されない限り、または
Access-Control-Allow-Credentials
と正しい
Access-Control-Allow-Origin
指定され
Access-Control-Allow-Origin
いない場合はサーバーに送信します)
Access-Control-Allow-Origin
:別のドメインへの
POST
リクエストの前に、ブラウザは最初にプリフライトリクエストを行い、許可されたサービス操作方法に関する情報を取得します。
WCF
このトピックには、さまざまな品質の一定量の情報があります。 残念ながら、WCFではこれらのヘッダーを標準的な方法で使用することはできませんが、この問題を解決するためのオプションがいくつかあります。 それらのいくつかをあなたの注意を喚起します。
web.configを使用したソリューション。
このソリューションでは、web.configに必要なヘッダーを直接追加します。
<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="http://foo.example" /> <add name="Access-Control-Allow-Headers" value="Content-Type" /> <add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS" /> <add name="Access-Control-Allow-Credentials" value="true" /> </customHeaders> </httpProtocol> </system.webServer>
それは、そのシンプルさと柔軟性のなさで際立っています。 特に、この特定の例は、可能性のあるドメインが複数ある場合は使用できません。さらに、サイト全体(特定の場合)へのCORSを許可します。
Global.asaxを使用したソリューション
このソリューションでは、必要なヘッダーを各リクエストに追加するコードをGlobal.asax.csで作成します。
protected void Application_BeginRequest(object sender, EventArgs e) { var allowedOrigins = new [] { "http://foo.example", "http://bar.example" }; var request = HttpContext.Current.Request; var response = HttpContext.Current.Response; var origin = request.Headers["Origin"]; if (origin != null && allowedOrigins.Any(x => x == origin)) { response.AddHeader("Access-Control-Allow-Origin", origin); response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); response.AddHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With"); response.AddHeader("Access-Control-Allow-Credentials", "true"); if (request.HttpMethod == "OPTIONS") { response.End(); } } }
このソリューションは複数のドメインをサポートしますが、サイト全体に適用されます。 もちろん、特定のサービスのすべての条件をその場で設定できますが、私の意見では、許可されたサービスのリストを維持するのに不便です。
WCFサービスコードにヘッダーを追加するソリューション
このソリューションは、特定のサービスまたはメソッドにヘッダーが追加されるという点でのみ、以前のソリューションと異なります。 一般的に、ソリューションは次のようになります。
[ServiceContract] public class MyService { [OperationContract] [WebInvoke(Method = "POST", ...)] public string DoStuff() { AddCorsHeaders(); return "<Data>"; } private void AddCorsHeaders() { var allowedOrigins = new [] { "http://foo.example", "http://bar.example" }; var request = WebOperationContext.Current.IncomingRequest; var response = WebOperationContext.Current.OutgoingResponse; var origin = request.Headers["Origin"]; if (origin != null && allowedOrigins.Any(x => x == origin)) { response.AddHeader("Access-Control-Allow-Origin", origin); response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); response.AddHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With"); response.AddHeader("Access-Control-Allow-Credentials", "true"); if (request.HttpMethod == "OPTIONS") { response.End(); } } } }
このアプローチにより、サービスのフレームワークまたはメソッドでのCORSの使用を制限できます。 主な欠点は、すべてのサービスメソッドで
AddCorsHeaders
を呼び出す必要
AddCorsHeaders
です。 プラス-使いやすさ。
ネイティブEndPointBehaviorとDispatchMessageInspectorを使用したソリューション
このアプローチでは、WCFの機能を使用して機能を拡張します。
EnableCorsBehavior
2つのクラスが作成されます。
using System; using System.ServiceModel.Channels; using System.ServiceModel.Configuration; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; namespace My.Web.Cors { public class EnableCorsBehavior : BehaviorExtensionElement, IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new EnableCorsMessageInspector()); } public void Validate(ServiceEndpoint endpoint) { } public override Type BehaviorType { get { return typeof(EnableCorsBehavior); } } protected override object CreateBehavior() { return new EnableCorsBehavior(); } } }
および
EnableCorsMessageInspector
:
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; namespace My.Web.Cors { public class EnableCorsMessageInspector : IDispatchMessageInspector { public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { var allowedOrigins = new [] { "http://foo.example", "http://bar.example" }; var httpProp = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; if (httpProp != null) { string origin = httpProp.Headers["Origin"]; if (origin != null && allowedOrigins.Any(x => x == origin)) { return origin; } } return null; } public void BeforeSendReply(ref Message reply, object correlationState) { string origin = correlationState as string; if (origin != null) { HttpResponseMessageProperty httpProp = null; if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name)) { httpProp = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name]; } else { httpProp = new HttpResponseMessageProperty(); reply.Properties.Add(HttpResponseMessageProperty.Name, httpProp); } httpProp.Headers.Add("Access-Control-Allow-Origin", origin); httpProp.Headers.Add("Access-Control-Allow-Credentials", "true"); httpProp.Headers.Add("Access-Control-Request-Method", "POST,GET,OPTIONS"); httpProp.Headers.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type"); } } } }
作成した
EnableCorsBehavior
を
web.config
追加します。
<system.serviceModel> ... <extensions> <behaviorExtensions> <add name="crossOriginResourceSharingBehavior" type="My.Web.Cors.EnableCorsBehavior, My.Web, Version=1.0.0.0, Culture=neutral" /> </behaviorExtensions> </extensions> ... </system.serviceModel>
EnableCorsBehavior
用に作成された拡張機能を
EnableCorsBehavior
て、
Endpoint
の
Behavior
構成に追加します
<system.serviceModel> <services> <service name="My.Web.Services.MyService"> <endpoint address="" behaviorConfiguration="My.Web.Services.MyService" binding="webHttpBinding" contract="My.Web.Services.MyService" /> </service> </services> ... <behaviors> ... <endpointBehaviors> ... <behavior name="My.Web.Services.MyService"> <webHttp/> <crossOriginResourceSharingBehavior /> <!-- --> </behavior> ... </endpointBehaviors> ... </behaviors> ... </system.serviceModel>
OPTIONS
メソッドで予備リクエストを処理するだけです。 私の場合、最も単純なオプションを使用しました
OPTIONS
要求ハンドラーメソッドがサービスの本体に追加されます。
[OperationContract] [WebInvoke(Method = "OPTIONS", UriTemplate = "*")] public void GetOptions() { // EnableCorsMessageInspector }
もちろん、プリフライトリクエストを処理するための同様のWCF拡張機能があります。そのうちの1つは、記事の最後にある参照リストから参照して読み取ることができます。 主な欠点は、サービスの本体に
GetOptions
メソッドを追加する必要があることと、かなりの量の追加コードです。 一方、このアプローチにより、サービスのロジックと通信のロジックをほぼ完全に分離できます。
Javascriptとブラウザに関するいくつかの言葉
承認およびCookieデータの送信をサポートするには、
withCredentials
フラグを
true
に設定する必要があります。 多くの人がjQueryを使用してAJAXを操作していると思うので、例を示します。
$.ajax({ type: 'POST', cache: false, dataType: 'json', xhrFields: { withCredentials: true }, contentType: 'application/json; charset=utf-8', url: options.serviceUrl + '/DoStuff' });
残念ながら、認証データの送信に関連する機能は、バージョン10以降のIEでのみ使用可能になりました。IE8/ 9ブラウザーはこの情報の送信をサポートせず、GETおよびPOSTでのみ機能します。
ログイン
上記のすべてのアプローチでは、メインサイトからの認証データが暗黙的に使用されます。 メインサイトでの承認
bar.other
bar.other
、サイトからのAjaxリクエストによりユーザーデータを返すことができます
foo.example
foo.example
私の場合、これはユーザーが同じビジネスプロジェクトの一部として存在しているが、異なるドメインとプラットフォームにあるサイトの1つにいる間に、通知を受信してイベントに応答できるようにするために使用されました。 前述のように、ここで重要なのはヘッダー
Access-Control-Allow-Credentials
と
XmlHttpRequest
フラグ
withCredentials=true
設定
withCredentials=true
。
ソースのリスト
- code.msdn.microsoft.com/windowsdesktop/Implementing-CORS-support-c1f9cd4b-すべての要求に対するMessageInspectorの完全な実装の例とその記事
- enable-cors.org/server_wcf.html-独自のBehaviorおよびMessageInspectorの作成に関するかなり短いマニュアルで、OPTIONSリクエストの機能を考慮せず、Originドメインチェック/選択の使用を許可しません
- developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS#Access-Control-Allow-Headers-CORSに関する最も包括的な情報源の1つ