つまり、このメソッドは、一方でRESTサービスを文書化するための優れたツールであり、他方でHATEOASのアーキテクチャ上の制限に大きく追加される可能性があります。
さて、「HATEOAS」のような怖い言葉から休憩して、自問してみましょう。WebアプリケーションでOPTIONSメソッドを使用することの実用的な利点はありますか?
そのため、少なくとも、OPTIONS要求への応答には、特定のエンドポイント、または単にUriに対して有効なメソッドのリストがAllowヘッダーに含まれている必要があります。
HTTP/1.1 200 OK
Allow: GET,POST,DELETE,OPTIONS
Content-Length: 0
それは何を与えますか?
リソースを投稿して操作できるWebアプリケーションがあるとします。 たとえば、Googleドキュメントのようなものです。 各ユーザーには、ドキュメントに対する特定の権限があります。誰かがそれを読むことができ、誰かがそれを編集でき、誰かがそれを削除できます。
ユーザーインターフェイスを開発するという課題に直面しています。 大まかに言えば、ある時点で、現在のユーザーの権限に応じて、[削除]ボタンを非表示にするか表示するかを決定する必要があります。
サービスから資格情報に関する知識を取得し、クライアント側でロジックを実装できます。 しかし、これは悪いアプローチです。
RESTアーキテクチャでは、クライアントとサーバーはできるだけ独立している必要があります。 ドキュメントを削除するためにユーザーに必要な権限を見つけることは、クライアントの仕事ではありません。 クライアントがそのロジックを実装している場合、サーバーで権限メカニズムを変更すると、クライアントで変更が必要になる可能性が高くなります。
UriによってドキュメントのOPTIONSをリクエストし、有効なメソッドのリストを取得できる場合、タスクは単純に解決されます。Allowヘッダーに「Delete」が含まれている場合、ボタンを表示するか、非表示にする必要があります。
OPTIONSメソッドの実装は簡単です。
[HttpOptions] [ResponseType(typeof(void))] [Route("Books", Name = "Options")] public IHttpActionResult Options() { HttpContext.Current.Response.AppendHeader("Allow", "GET,OPTIONS"); return Ok(); }
しかし、各コントローラーと各ルートにメソッドを実装することは困難です。 問題は、この経済を維持することは非常に面倒であるということです。適切なメソッドをコントローラーに追加するのを忘れたり、ルーティングの変更を間違えたりするのは非常に簡単です。
プロセスを自動化することは可能ですか?
ASP.NET Web APIは、自動化のための良い機会を提供します: HTTPメッセージハンドラー 。
DelegatingHandlerクラスから新しいハンドラーを継承し、 SendAsyncメソッドをオーバーロードし、タスクの継続として新しい機能を追加します。 最初に基本的なルーティングメカニズムを開始するため、これは重要です。 この場合、 要求変数には必要なすべてのプロパティが含まれます。
protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { return await base.SendAsync(request, cancellationToken).ContinueWith( task => { var response = task.Result; if (request.Method == HttpMethod.Options) { var methods = new ActionSelector(request).GetSupportedMethods(); if (methods != null) { response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; response.Content.Headers.Add("Allow", methods); response.Content.Headers.Add("Allow", "OPTIONS"); } } return response; }, cancellationToken); }
ActionSelectorクラスは 、コンストラクター内の要求に適したコントローラーを見つけようとしています。 コントローラーが見つからない場合、 GetSupportedMethodsメソッドはnullを返します。 IsMethodSupported関数は、指定された名前でアクションメソッドを見つけるために、コンテキスト内の現在の要求を置き換えます。 _apiSelector.SelectActionを呼び出すとこのコンテキストプロパティを変更できるため、finallyブロックはRouteDataを復元します。
private class ActionSelector { private readonly HttpRequestMessage _request; private readonly HttpControllerContext _context; private readonly ApiControllerActionSelector _apiSelector; private static readonly string[] Methods = { "GET", "PUT", "POST", "PATCH", "DELETE", "HEAD", "TRACE" }; public ActionSelector(HttpRequestMessage request) { try { var configuration = request.GetConfiguration(); var requestContext = request.GetRequestContext(); var controllerDescriptor = new DefaultHttpControllerSelector(configuration) .SelectController(request); _context = new HttpControllerContext { Request = request, RequestContext = requestContext, Configuration = configuration, ControllerDescriptor = controllerDescriptor }; } catch { return; } _request = _context.Request; _apiSelector = new ApiControllerActionSelector(); } public IEnumerable<string> GetSupportedMethods() { return _request == null ? null : Methods.Where(IsMethodSupported); } private bool IsMethodSupported(string method) { _context.Request = new HttpRequestMessage( new HttpMethod(method), _request.RequestUri); var routeData = _context.RouteData; try { return _apiSelector.SelectAction(_context) != null; } catch { return false; } finally { _context.RouteData = routeData; } } }
最後の手順は、スタートアップコードの構成にハンドラーを追加することです。
configuration.MessageHandlers.Add(new OptionsHandler());
すべてが正しく機能するためには、パラメーターのタイプを明示的に指定する必要があります。 Route属性を使用する場合、タイプを直接指定する必要があります。
[Route("Books/{id:long}", Name = "GetBook")]
タイプを明示的に指定しない場合、 Books / abcdパスは有効と見なされます。
したがって、サービスでサポートされているすべてのUriのOPTIONS実装ができました。 ただし、これはまだ理想的なソリューションではありません。 説明されているアプローチでは、承認は考慮されていません。 アクセストークンを使用し、サービスへの各呼び出しが現在のプリンシパルの存在を提供する場合、その権利は一切考慮されず、OPTIONSメソッドの値は大幅に減少します。 すべてのユーザーに対して、クライアントは、権限に関係なく、常に有効なHTTPメソッドの同じリストを受け取ります。
これを回避するには、必要に応じて、HttpOptions実装をコントローラーに手動で追加します。 これらのアクションメソッドは、有効なメソッドのリストを作成するときにユーザー権限を確認する必要があります。 この場合、OptionsHandlerはコントローラーからの応答があればそれを使用する必要があります。
ただし、CORSには問題があります。 クロスドメインリクエストの場合、ブラウザはリクエストされたアドレスにOPTIONSリクエストを個別に送信するため、Authorizationヘッダーをインストールする方法はありません。
したがって、許可されたユーザーと許可されていないユーザーの両方に対して、サービスにOPTIONSを実装する必要があります。 最初のケースでは、現在のユーザーの権利を考慮し、2番目のケースでは、すべての有効なメソッドを返す必要があります。
→ 最終コード
著者の翻訳