2016-06-01 4 views
10

ビジネスレイヤで定義されているメッセージハンドラ実装に着信メッセージをディスパッチする2つ以上の実装を含む、非常に薄いインフラストラクチャのWeb APIがあります。つまり、コントローラはなく、コントローラのアクションもありません。 APIはメッセージにのみ基づいて定義されます。つまり、新しい機能が実装されている場合、このインフラストラクチャ層でコード変更は必要ありません。メッセージディスパッチャとしてDelegatingHandlerを使用したSwashbuckleのコンフィグレーション

は例えば、我々のようなメッセージを持っている:URLに基​​づいてメッセージを正確に決定GetUnshippedOrdersForCurrentCustomerQuery

委任ハンドラGetOrderByIdQuery

  • をShipOrderCommand
    • CreateOrderCommandをし、リクエストの内容は、thaのインスタンスにデシリアライズされます。そのメッセージは適切なメッセージハンドラに転送されます。例えば、これらのメッセージは、(現在は)以下のURLにマッピングされている:

      • API /コマンド/ CreateOrder
      • API /コマンド/ ShipOrder
      • API /クエリ/ GetOrderById
      • API /クエリ/ GetUnshippedOrdersForCurrentCustomer

      Web APIでこのように作業することで、開発が容易になり、開発のパフォーマンスが向上します。書き込むコードが少なく、テストするコードが少なくて済みます。

      コントローラがないので、私はこの問題をSwashbuckleでブートストラップしています。ドキュメントを読んだ後、私はSwashbuckleでこれらの種類のURLを登録する方法を見つけませんでした。 SwashbuckleをAPIドキュメントを出力できるように設定する方法はありますか?

      完全性のために、これを実証する参照アーキテクチャアプリケーションはhereです。

  • +0

    必要ですドキュメンテーション? – Evk

    +0

    @Evkスワッシュバックルを伸ばすことは絶対にありません。 – Steven

    +0

    メッセージハンドラをどのように文書化していますか?いくつかのカスタム属性でメッセージクラス自体を飾る? – Evk

    答えて

    9

    Swashbuckleはこれをそのまま受け入れることはできませんが、希望の結果を達成するために拡張することはできますが、大部分のインフラストラクチャをまだ再利用しています。しかし、この回答には完全な解決策を提示するにはあまりにも多くの時間と努力が必要です。しかし、私は少なくともあなたを始めさせようとします。以下のすべてのコードはあまりクリーンでなく、生産準備が整っていないことに注意してください。

    最初に必要なものは、カスタムIApiExplorerを作成して登録することです。これはSwashbuckleがAPIの説明を取得するために使用するインターフェースで、必要な情報を収集するためにすべてのコントローラーとアクションを調べます。基本的には既存のApiExplorerをコードで拡張してメッセージクラスを探求し、APIクラスの説明を作成します。インターフェイス自体は単純です:

    public interface IApiExplorer 
    {  
        Collection<ApiDescription> ApiDescriptions { get; } 
    } 
    

    API説明クラスは、API操作に関する様々な情報が含まれており、それが闊歩UIページを構築するためにSwashbuckleで使用されるものです。 1つの問題のあるプロパティ:ActionDescriptorがあります。これは、asp.net mvcアクションを表し、我々はアクションを持っていない、我々はコントローラを持っていない。あなたは、それの偽の実装を使用するか、asp.net HttpActionDescriptorの動作を模倣し、実際の値を提供することができます。簡単にするために、我々は最初のルートに移動します:

    class DummyActionDescriptor : HttpActionDescriptor { 
        public DummyActionDescriptor(Type messageType, Type returnType) { 
         this.ControllerDescriptor = new DummyControllerDescriptor() { 
          ControllerName = "Message Handlers" 
         }; 
         this.ActionName = messageType.Name; 
         this.ReturnType = returnType; 
        } 
    
        public override Collection<HttpParameterDescriptor> GetParameters() { 
         // note you might provide properties of your message class and HttpParameterDescriptor here 
         return new Collection<HttpParameterDescriptor>(); 
        } 
    
        public override string ActionName { get; } 
        public override Type ReturnType { get; } 
    
        public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken) { 
         // will never be called by swagger 
         throw new NotSupportedException(); 
        } 
    } 
    
    class DummyControllerDescriptor : HttpControllerDescriptor { 
        public override Collection<T> GetCustomAttributes<T>() { 
         // note you might provide some asp.net attributes here 
         return new Collection<T>(); 
        } 
    } 
    

    ここでは威張っが呼び出すと、我々は彼らのために値を提供しなかった場合に失敗しますのみいくつかのオーバーライドを提供しています。今

    のは、とのメッセージクラスを飾るためにいくつかの属性を定義してみましょう:

    class MessageAttribute : Attribute { 
        public string Url { get; } 
        public string Description { get; } 
        public MessageAttribute(string url, string description = null) { 
         Url = url; 
         Description = description; 
        } 
    } 
    
    class RespondsWithAttribute : Attribute { 
        public Type Type { get; } 
        public RespondsWithAttribute(Type type) { 
         Type = type; 
        } 
    } 
    

    そして、いくつかのメッセージ:

    abstract class BaseMessage { 
    
    } 
    
    [Message("/api/commands/CreateOrder", "This command creates new order")] 
    [RespondsWith(typeof(CreateOrderResponse))] 
    class CreateOrderCommand : BaseMessage { 
    
    } 
    
    class CreateOrderResponse { 
        public long OrderID { get; set; } 
        public string Description { get; set; } 
    } 
    

    今カスタムApiExplorer:

    class MessageHandlerApiExplorer : IApiExplorer { 
        private readonly ApiExplorer _proxy; 
    
        public MessageHandlerApiExplorer() { 
         _proxy = new ApiExplorer(GlobalConfiguration.Configuration); 
         _descriptions = new Lazy<Collection<ApiDescription>>(GetDescriptions, true); 
        } 
    
        private readonly Lazy<Collection<ApiDescription>> _descriptions; 
    
        private Collection<ApiDescription> GetDescriptions() { 
         var desc = _proxy.ApiDescriptions; 
         foreach (var handlerDesc in ReadDescriptionsFromHandlers()) { 
          desc.Add(handlerDesc); 
         } 
         return desc; 
        } 
    
        public Collection<ApiDescription> ApiDescriptions => _descriptions.Value; 
    
        private IEnumerable<ApiDescription> ReadDescriptionsFromHandlers() { 
         foreach (var msg in Assembly.GetExecutingAssembly().GetTypes().Where(c => typeof(BaseMessage).IsAssignableFrom(c))) { 
          var urlAttr = msg.GetCustomAttribute<MessageAttribute>(); 
          var respondsWith = msg.GetCustomAttribute<RespondsWithAttribute>(); 
          if (urlAttr != null && respondsWith != null) { 
           var desc = new ApiDescription() { 
            HttpMethod = HttpMethod.Get, // grab it from some attribute 
            RelativePath = urlAttr.Url, 
            Documentation = urlAttr.Description, 
            ActionDescriptor = new DummyActionDescriptor(msg, respondsWith.Type) 
           };      
    
           var response = new ResponseDescription() { 
            DeclaredType = respondsWith.Type, 
            ResponseType = respondsWith.Type, 
            Documentation = "This is some response documentation you grabbed from some other attribute" 
           };      
           desc.GetType().GetProperty(nameof(desc.ResponseDescription)).GetSetMethod(true).Invoke(desc, new object[] {response}); 
           yield return desc; 
          } 
         } 
        } 
    } 
    

    そして最後に登録IApiExplorer(後スワッガーを登録しました):

    GlobalConfiguration.Configuration.Services.Replace(typeof(IApiExplorer), new MessageHandlerApiExplorer()); 
    

    このすべてを行った後、私たちは闊歩インターフェイスで、当社のカスタムメッセージのコマンドを見ることができます:あなたは、いくつかの既製のソリューション、またはカスタムハンドラを含めるようにSwashbuckleを拡張するだけの方法を

    enter image description here

    +0

    私はあなたのコードを試してみましたが、MSDNのドキュメントで 'ResponseDescription'タイプとプロパティを見つけることはできますが、最新の' System.Web.Http.dll'にはどこも見つかりません。奇妙な事実。 – Steven

    +0

    どのプラットフォームを使用しますか?上記のコードをテストするのを忘れずに、通常の.net 4.5を使用しました。 – Evk

    +0

    .NET 4.5。 System.Web.Netのどのバージョンを使用しましたか? – Steven

    関連する問題