2016-06-01 9 views
10

Mam Web API, który jest naprawdę cienką częścią infrastruktury, która zawiera tylko dwie implementacje DelegatingHandler, które wysyłają wiadomości przychodzące do implementacji obsługi komunikatów zdefiniowanych w warstwie biznesowej. Oznacza to, że nie ma żadnych Kontrolerów i żadnych działań kontrolera; API definiuje się wyłącznie na podstawie komunikatów. Oznacza to, że zmiany kodu w tej warstwie infrastruktury nie są wymagane po wdrożeniu nowych funkcji.Konfigurowanie SwashBuckle z DelegatingHandler jako dyspozytorem komunikatów

Na przykład, mamy komunikaty takie jak:

  • CreateOrderCommand
  • ShipOrderCommand
  • GetOrderByIdQuery
  • GetUnshippedOrdersForCurrentCustomerQuery

Delegujące koparki określić dokładną wiadomość na podstawie adresu URL, a treść żądania jest przekształcona do postaci instancji tha t typ komunikatu, po którym wiadomość ta jest przekazywana do odpowiedniej procedury obsługi komunikatów. Na przykład, te wiadomości są (obecnie) przyporządkowuje się do następujących adresów URL:

  • api/POLECENIA/CreateOrder
  • API/polecenia/ShipOrder
  • API/zapytań/GetOrderById
  • api/zapytań/GetUnshippedOrdersForCurrentCustomer

Jak można sobie wyobrazić, ten sposób pracy z Web API upraszcza rozwój i zwiększa wydajność rozwoju; jest mniej kodu do napisania i mniej kodu do przetestowania.

Ale ponieważ nie ma kontrolerów, mam problem z załadowaniem tego w Swashbuckle; po przeczytaniu dokumentacji nie znalazłem sposobu na zarejestrowanie tego rodzaju adresów URL w Swashbuckle. Czy istnieje sposób na skonfigurowanie Swashbuckle w taki sposób, aby mógł on nadal wyświetlać dokumentację API?

Aby uzyskać kompletność, można znaleźć aplikację architektury referencyjnej, która to demonstruje: here.

+0

Czy trzeba jakieś gotowe rozwiązanie, czy tylko sposób, aby przedłużyć Swashbuckle zawierać niestandardowe teleskopowe dokumentacja? – Evk

+0

@Evk rozszerzenie Swashbuckle absolutnie zrobić. – Steven

+0

A jak Ty (lub zamierzasz) udokumentować obsługę wiadomości? Udekorować same klasy wiadomości za pomocą niestandardowych atrybutów? – Evk

Odpowiedz

9

Wygląda na to, że Swashbuckle nie obsługuje tego po wyjęciu z pudełka, ale można go rozszerzyć, aby osiągnąć pożądany wynik, jednocześnie nadal wykorzystując większość infrastruktury typu "swagger". Może to zająć trochę czasu i wysiłków, niewiele w ogóle, ale zbyt wiele dla mnie, aby zapewnić kompletne rozwiązanie w tej odpowiedzi. Jednak postaram się przynajmniej zacząć. Uwaga: poniższy kod nie będzie bardzo czysty i produkcja będzie gotowa.

Najpierw należy utworzyć i zarejestrować niestandardowe IApiExplorer. Jest to interfejs używany przez Swashbuckle do pobierania opisów api i jest odpowiedzialny za zbadanie wszystkich kontrolerów i działań w celu zebrania wymaganych informacji. Zasadniczo rozszerzymy istniejący ApiExplorer o kod, aby zbadać nasze klasy wiadomości i zbudować z nich opis api. Sam interfejs jest prosty: Opis

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

Api klasa zawiera różne informacje na temat działania substancji czynnych i to, co jest wykorzystywane przez Swashbuckle zbudować Swagger stronę UI. Ma jedną problematyczną właściwość: ActionDescriptor. Reprezentuje akcję asp.net mvc, a my nie mamy akcji, nie mamy kontrolerów.Możesz użyć fałszywej implementacji tego lub imitować zachowanie asp.net HttpActionDescriptor i podać prawdziwe wartości. Dla uproszczenia będziemy iść z pierwszej trasy:

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>(); 
    } 
} 

Tutaj zapewniamy tylko niektóre przesłonięcia że Swagger będzie zadzwonić i mogą zostać uszkodzone, jeśli nie określają wartości dla nich.

Teraz zdefiniować pewne atrybuty do dekorowania klas wiadomości z:

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; 
    } 
} 

a niektóre wiadomości:

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; } 
} 

Teraz zwyczaj 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; 
      } 
     } 
    } 
} 

I wreszcie zarejestrować IApiExplorer (po zarejestrowałeś swój przedmiot Swagger) za pomocą:

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

Po zrobieniu tego wszystkiego widzimy naszą komendę niestandardowych komunikatów w interfejsie Swagger:

enter image description here

+0

Eksperymentowałem z twoim kodem, ale chociaż mogę znaleźć typ i typ "ResponseDescription" w dokumentach MSDN, jego nigdzie nie można znaleźć w najnowszym 'System.Web.Http.dll'. Dziwne gówno. – Steven

+0

Jakiej platformy używasz? Użyłem zwykłego .net 4.5, ponieważ pamiętam, aby przetestować powyższy kod. – Evk

+0

.NET 4.5. Z której wersji System.Web.Net korzystałeś? – Steven

Powiązane problemy