WebAPI用のSwashbuckle(Swagger)の構成

WebAPIをテストしたことがある人なら誰でも、PostmanやAdvanced REST(Chromeの拡張機能)などのツールを知っています。 これらのツールは誰にとっても便利ですが、APIが受け入れるモデルを認識する方法がわからず、すべての可能なエンドポイントに関する情報を提供および提供しません。 この不都合は、Swagger仕様とUIの生成をプロジェクトに統合するSwashbuckleパッケージによって解決されます。 猫の下で、プロジェクトにそれを固定する方法について簡単に説明し、「オーバーロードされた」エンドポイントでの承認と操作に関する詳細を説明します。



プロジェクトに留める



Swashbuckleは、 OpenAPI仕様に従ってノード生成情報をWebAPIに統合するNuGetパッケージです。 この仕様は、事実上、かつてWSDLだった標準です。 インストールするには、4つの簡単な手順が必要です。



  1. Install Install-Package Swashbuckle



    コマンドを使用してNuGetからインストールする
  2. プロジェクト設定にXMLドキュメントを含めます
  3. パッケージのインストール時に作成されるSwaggerConfig.cs



    ファイルで、 c.IncludeXmlComments(GetXmlCommentsPath());



    行のコメントを解除しc.IncludeXmlComments(GetXmlCommentsPath());



  4. GetXmlCommentsPath()



    メソッドの実装で、 return string.Format(@"{0}\bin\BookStoreApiService.XML", AppDomain.CurrentDomain.BaseDirectory);



    記述しreturn string.Format(@"{0}\bin\BookStoreApiService.XML", AppDomain.CurrentDomain.BaseDirectory);





それだけです 次に、APIメソッド、応答コードを説明し、さらにカスタマイズする必要があります。



WebAPI展開のニュアンス



実稼働環境でWebAPIをデプロイする場合、XMLファイルの欠落に問題がある可能性があります。 リリースビルドにはデフォルトでは含まれていませんが、csprojファイルを編集することで回避できます。 プロジェクトのPropertyGroupに<ExcludeXmlAssemblyFiles>false</ExcludeXmlAssemblyFiles>



を追加する必要があり、ファイルはbin/



残ります。



もう1つの問題は、プロキシの背後にAPIを隠す人を待つことにあります。 解決策は普遍的ではありませんが、私の場合はうまくいきます。 プロキシはリクエストにヘッダーを追加し、クライアントのエンドポイントURLを確認します。



プロキシのURL認識の例
 //   SwaggerConfig.cs c.RootUrl(req => ComputeClientHost(req)); //     public static string ComputeClientHost(HttpRequestMessage req) { var authority = req.RequestUri.Authority; var scheme = req.RequestUri.Scheme; //  ,    if (req.Headers.Contains("X-Forwarded-Host")) { //          var xForwardedHost = req.Headers.GetValues("X-Forwarded-Host").First(); var firstForwardedHost = xForwardedHost.Split(',')[0]; authority = firstForwardedHost; } //  ,    if (req.Headers.Contains("X-Forwarded-Proto")) { var xForwardedProto = req.Headers.GetValues("X-Forwarded-Proto").First(); xForwardedProto = xForwardedProto.Split(',')[0]; scheme = xForwardedProto; } return scheme + "://" + authority; }
      
      







応答コードを追加する



返されるHTTPステータスコードは、XMLコメントの使用と属性の使用の2つの方法で追加できます。



ステータスコードを追加する例
 /// <response code="404">Not Found</response> [SwaggerResponse(HttpStatusCode.NotFound, Type = typeof(Model), Description = "Not Found: no such endpoint")]
      
      







ただし、XMLコメントは属性よりも優先されることに注意してください。 同じメソッドに対して2つのメソッドが同時に使用される場合、後者は無視されます。 また、XMLコメントを使用する場合、200(OK)を含むすべてのコードを指定する必要があり、返されるモデルを指定することはできません。 したがって、SwaggerResponseを使用することをお勧めします。 彼はこれらの欠点を欠いています。 エンドポイントがデフォルトの200ではなく201(作成済み)などの別のコードを返す場合、最初の[SwaggerResponseRemoveDefaults]



[SwaggerResponseRemoveDefaults]



属性で削除する必要があります。



怠け者の場合、すべてのメソッドに共通のコード(400(BadRequest)または401(Unauthorized)など)を一度に追加できます。 これを行うには、IOperationFilterインターフェイスを実装し、c.OperationFilter <T>();を使用してそのようなクラスを登録する必要があります。



コードのリストを追加するApplyメソッドの例
 HttpStatusCode[] _codes; //    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) { //      if (operation.responses == null) operation.responses = new Dictionary<string, Response>(); foreach (var code in _codes) { var codeNum = ((int)code).ToString(); var codeName = code.ToString(); //   if (!operation.responses.ContainsKey(codeNum)) operation.responses.Add(codeNum, new Response { description = codeName }); } }
      
      







WebAPIおよびスワッシュバックル認証



以下のテキストでは、基本認証を実装するためのいくつかのオプションについて説明しています。 しかし、このパッケージは他のものもサポートしています。


AuthorizeAttributeを使用すると、SwashbuckleはUIを構築しますが、リクエストは失敗します。 この情報を提供する方法はいくつかあります。



  1. ブラウザに組み込まれた承認を介して
  2. パッケージに組み込まれている認証フォームを介して
  3. 操作パラメーターを通して
  4. JavaScriptを介して


内蔵ブラウザ



属性とフィルタが使用されている場合、ブラウザに組み込まれた承認は「そのまま」利用できます。



 // Basic Authorization attributes config.Filters.Add(new AuthorizeAttribute()); config.Filters.Add(new BasicAuthenticationFilter()); //  IAuthenticationFilter
      
      





これらをWebAPI構成に追加すると、ブラウザーは要求が行われたときに認証情報の入力を求めるプロンプトを表示します。 ここでの難点は、このデータをリセットすることは、入力するほど便利で高速ではないことです。



インラインスワッシュバックルログインフォーム



この点で別の方法がより便利です。 特別な形式を提供します。 組み込みの認証フォームをパッケージに含めるには、次を実行する必要があります。



  1. 上記のように、認証の属性とフィルターを有効にします
  2. Swagger設定で、 c.BasicAuth("basic").Description("Basic HTTP Authentication");



    の行のコメントをc.BasicAuth("basic").Description("Basic HTTP Authentication");



  3. これに関する情報をノードに追加する特別なIOperationFilterを追加しますc.OperationFilter<MarkSecuredMethodsOperationFilter>();





このフィルターのApplyメソッドの実装
 public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) { var filterPipeline = apiDescription.ActionDescriptor.GetFilterPipeline(); // check if authorization is required var isAuthorized = filterPipeline .Select(filterInfo => filterInfo.Instance) .Any(filter => filter is IAuthorizationFilter); // check if anonymous access is allowed var allowAnonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any(); if (isAuthorized && !allowAnonymous) { if (operation.security == null) operation.security = new List<IDictionary<string, IEnumerable<string>>>(); var auth = new Dictionary<string, IEnumerable<string>> { {"basic", Enumerable.Empty<string>()} }; operation.security.Add(auth); } }
      
      







その後、この形式の認証を使用することが可能になり、入力されたデータはすべてのリクエストに使用されます。







パラメーターとJSコードによる承認



IOperationFilterを使用してJavaScriptを挿入する例として、次の2つのメソッドを検討する必要があります。


パラメーターは、本文とクエリだけでなく、ヘッダーでもデータを送信できます。 この場合、ハッシュを入力する必要があります。



そのようなパラメーターを追加する
 operation.parameters.Add(new Parameter { name = "Authorization", @in = "header", // ,      description = "Basic U3dhZ2dlcjpUZXN0", // Basic Swagger:Test required = true, //   type = "string" });
      
      







JavaScriptを挿入することにより、リクエストヘッダーにデータを送信することもできます。 これを行うには、次を実行します。



  1. jsファイルを埋め込みリソースとして追加します
  2. Swagger構成で、行のコメントを外し、リソース名としてファイルを指定します: c.InjectJavaScript(thisAssembly, "assembly.namesapce.swagger-basic-auth.js");



  3. このようなファイルに書き込みます: swaggerUi.api.clientAuthorizations.add("basic", new SwaggerClient.ApiKeyAuthorization("Authorization", "Basic U3dhZ2dlcjpUZXN0", "header"));





これで、このデータは各リクエストのヘッダーとして追加されます。 一般に、このJSコードを使用すると、私が理解しているように、任意のヘッダーを送信できます。 この例では「basic」に等しいキーパラメータは、リクエストの送信時にJSエラーがポップアップしないように一意である必要があります。



たとえば、JSがSwaggerにヘッダーを送信する
 swaggerUi.api.clientAuthorizations.add("custom1", new SwaggerClient.ApiKeyAuthorization("X-Header-1", "value1", "header")); swaggerUi.api.clientAuthorizations.add("custom2", new SwaggerClient.ApiKeyAuthorization("X-Header-2", "value2", "header")); swaggerUi.api.clientAuthorizations.add("custom3", new SwaggerClient.ApiKeyAuthorization("X-Header-3", "value3", "header"));
      
      







必須ヘッドの使用



場合によっては、不正なヘッダーが必要になることがあります。 たとえば、顧客情報を含むヘッダー。 通常、メッセージハンドラーはWebAPIパイプラインに組み込まれます。つまり、DelegatingHandlerが実装され、WebAPI構成config.MessageHandlers.Add(new MandatoryHeadersHandler());



登録されますconfig.MessageHandlers.Add(new MandatoryHeadersHandler());



。 この場合、Swaggerは何も表示しなくなります。 それへの要求は渡されません、なぜなら ハンドラーはそれらを禁止します。 箱から出して、これを解決することはできませんので、ハンドラーでこのケースを提供する必要があります。 つまり URLを変更するリクエストの場合は、スキップします。 上記のように、JSを使用してヘッダーを追加すると役立ちます。



オーバーロードされたメソッドエンドポイント



WebAPIを使用すると、1つのエンドポイントに対して複数のアクションメソッドを作成でき、その呼び出しはリクエストパラメーターに依存します。



 [ResponseType(typeof (IList<Model>))] public IHttpActionResult Get() {...} [ResponseType(typeof (IList<Model>))] public IHttpActionResult Get(int count, bool descending) {...}
      
      





そのようなメソッドはデフォルトではSwaggerでサポートされておらず、UIは500エラーをスローします:Swagger 2.0ではサポートされていません:パス 'api / <URL>'およびメソッド '<METHOD>'の複数の操作。 考えられる回避策については、構成設定-\ "ResolveConflictingActions \"を参照してください。



メッセージに示されているように、状況を個別に解決する必要があり、いくつかのオプションがあります。



  1. 1つの方法のみを選択してください
  2. すべてのパラメーターで1つのメソッドを作成します
  3. ドキュメント生成の変更


最初と2番目のメソッドは、 c.ResolveConflictingActions(Func<IEnumerable<ApiDescription>, ApiDescription> conflictingActionsResolver)



設定することで実装されます。 メソッドの本質は、競合するいくつかのメソッドを取得して返すことです。



すべてのパラメーターを組み合わせる方法の例
 return apiDescriptions => { var descriptions = apiDescriptions as ApiDescription[] ?? apiDescriptions.ToArray(); var first = descriptions.First(); //     var parameters = descriptions.SelectMany(d => d.ParameterDescriptions).ToList(); first.ParameterDescriptions.Clear(); //        foreach (var parameter in parameters) if (first.ParameterDescriptions.All(x => x.Name != parameter.Name)) { first.ParameterDescriptions.Add(new ApiParameterDescription { Documentation = parameter.Documentation, Name = parameter.Name, ParameterDescriptor = new OptionalHttpParameterDescriptor((ReflectedHttpParameterDescriptor) parameter.ParameterDescriptor), Source = parameter.Source }); } return first; }; //   , .. IsOptional   getter public class OptionalHttpParameterDescriptor : ReflectedHttpParameterDescriptor { public OptionalHttpParameterDescriptor(ReflectedHttpParameterDescriptor parameterDescriptor) : base(parameterDescriptor.ActionDescriptor, parameterDescriptor.ParameterInfo) { } public override bool IsOptional => true; }
      
      







カーディナルウェイ



3番目の方法はより基本的で、OpenAPI仕様からの脱却です。 パラメーターを使用してすべてのエンドポイントを表示できます。







これを行うには、IDocumentFilterを使用してSwaggerドキュメントの生成方法を変更し、説明を自分で生成する必要があります。



人生では、この方法はめったに必要ないので、さらに深く掘り下げます。 Swashbuckleの内部構造に興味がある人にのみお勧めする別の方法は、SwaggerGeneratorを置き換えることです。 これは、行c.CustomProvider(defaultProvider => new NewSwaggerProvider(defaultProvider));



。 これを行うには、これを行うことができます:



  1. クラスMySwaggerGeneratorを作成します:ISwaggerProvider
  2. GitHubのSwashbuckleリポジトリでSwaggerGenerator.csを見つけます( ここにあります
  3. GetSwaggerメソッドとその他の関連メソッドを
  4. 内部変数を複製し、クラスのコンストラクターで初期化します
  5. Swagger構成で登録する


内部変数の初期化
 private readonly IApiExplorer _apiExplorer; private readonly IDictionary<string, Info> _apiVersions; private readonly JsonSerializerSettings _jsonSerializerSettings; private readonly SwaggerGeneratorOptions _options; public MultiOperationSwaggerGenerator(ISwaggerProvider sp) { var sg = (SwaggerGenerator) sp; var privateFields = sg.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic); _apiExplorer = privateFields.First(pf => pf.Name == "_apiExplorer").GetValue(sg) as IApiExplorer; _jsonSerializerSettings = privateFields.First(pf => pf.Name == "_jsonSerializerSettings").GetValue(sg) as JsonSerializerSettings; _apiVersions = privateFields.First(pf => pf.Name == "_apiVersions").GetValue(sg) as IDictionary<string, Info>; _options = privateFields.First(pf => pf.Name == "_options").GetValue(sg) as SwaggerGeneratorOptions; }
      
      







その後、場所を見つけますvar paths = GetApiDescriptionsFor(apiVersion)....



これはパスが作成される場所です。 たとえば、例の内容を取得するには、GroupBy()を.GroupBy(apiDesc => apiDesc.RelativePath)



に置き換える必要があります。



文学



  1. Swaggerの例
  2. RESTful Web API仕様フォーマット
  3. Swashbuckleで生成されたAPI定義をカスタマイズする
  4. Swaggerオブジェクトスキーマ
  5. ASP.NET Web API 2の認証フィルター
  6. WebAPI基本認証承認フィルター
  7. Swashbuckleを使用してSwaggerUIで認証ヘッダーをカスタマイズする
  8. ASP.NET Web APIのHTTPメッセージハンドラー
  9. Swashbuckleを使用したASP.Net 5でのアクションの競合の管理
  10. GitHubのチュートリアルSwaggerプロジェクト



All Articles