2016-03-23 31 views
7

Sto cercando un modo per mostrare/nascondere le rotte WebAPI nella documentazione Swagger usando SwashBuckle in un modo configurabile. Aggiungere [ApiExplorerSettings(IgnoreApi = true)] nasconderà effettivamente i percorsi ma dovrei ricompilare ogni volta che voglio che cambi.Rimuovere un percorso con IOperationFilter in SwashBuckle

Ho cercato di creare un IOperationFilter per lavorare con un attributo personalizzato che ho definito. In questo modo posso decorare i percorsi con un [SwaggerTag("MobileOnly")] e controllare il web.config o qualcosa del genere per vedere se il percorso deve essere mostrato. L'attributo è definito come tale:

public class SwaggerTagAttribute : Attribute 
{ 
    public string[] Tags { get; private set; } 

    public SwaggerTagAttribute(params string[] tags) 
    { 
     this.Tags = tags; 
    } 
} 

Il IOperationFilter che rileva l'attributo è definito e il IDocumentFilter che rimuove il percorso è definito qui:

public class RemoveTaggedOperationsFilter : IOperationFilter, IDocumentFilter 
{ 
    private List<string> TagsToHide; 

    public RemoveTaggedOperationsFilter() 
    { 
     TagsToHide = ConfigurationManager.AppSettings["TagsToHide"].Split(',').ToList(); 
    } 

    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) 
    { 
     var tags = apiDescription.ActionDescriptor 
      .GetCustomAttributes<SwaggerTagAttribute>() 
      .Select(t => t.Tags) 
      .FirstOrDefault(); 

     if (tags != null && TagsToHide.Intersect(tags).Any()) 
     { 
      operation.tags = new List<string> {"Remove Me "}; 
     } 
    } 

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer) 
    { 
     foreach (var value in swaggerDoc.paths.Values) 
     { 
      if (value.post != null && value.post.tags.Contains("Remove Me")) 
       value.post = null; 

      if (value.get != null && value.get.tags.Contains("Remove Me")) 
       value.get = null; 

      if (value.put != null && value.put.tags.Contains("Remove Me")) 
       value.put = null; 

      if (value.delete != null && value.delete.tags.Contains("Remove Me")) 
       value.delete = null; 
     } 
    } 
} 

registrato in quanto tale:

GlobalConfiguration.Configuration 
      .EnableSwagger(c => 
       { 
        c.OperationFilter<RemoveTaggedOperationsFilter>(); 
        c.DocumentFilter<RemoveTaggedOperationsFilter>(); 
       }); 

Ritengo che questo sia inefficiente e hacky per taggare qualcosa per la rimozione in un secondo momento, quando potrò accedervi in ​​precedenza. Esiste un modo per rimuovere semplicemente la rotta dall'interno di IOperationFilter.Apply anziché attendere lo IDocumentFilter e scansionarla?

risposta

3

Qualcuno aveva già inviato una risposta in precedenza e ha detto che avrebbe inserito il codice una volta ottenuta la possibilità. Hanno cancellato la loro risposta per qualche motivo ma mi hanno portato a una soluzione migliore.

Piuttosto che usare IOperationFilter per contrassegnare il percorso e poi IDocumentFilter per rimuovere il percorso in seguito si può semplicemente utilizzare IDocumentFilter per trovare l'attributo personalizzato e rimuoverlo in un colpo solo. Il codice è qui sotto:

public class HideTaggedOperationsFilter : IDocumentFilter 
{ 
    private List<string> TagsToHide; 

    public HideTaggedOperationsFilter() 
    { 
     TagsToHide = ConfigurationManager.AppSettings["TagsToHide"].Split(',').ToList(); 
    } 

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer) 
    { 
     if (_tagsToHide == null) return; 

     foreach (var apiDescription in apiExplorer.ApiDescriptions) 
     { 
      var tags = apiDescription.ActionDescriptor 
       .GetCustomAttributes<SwaggerTagAttribute>() 
       .Select(t => t.Tags) 
       .FirstOrDefault(); 

      if (tags == null || !_tagsToHide.Intersect(tags).Any()) 
       continue; 

      var route = "/" + apiDescription.Route.RouteTemplate.TrimEnd('/'); 
      swaggerDoc.paths.Remove(route); 
     } 
    } 
} 

public class SwaggerTagAttribute : Attribute 
{ 
    public string[] Tags { get; } 

    public SwaggerTagAttribute(params string[] tags) 
    { 
     this.Tags = tags; 
    } 
} 

registrare il IDocumentFilter:

GlobalConfiguration.Configuration.EnableSwagger(c => 
{ 
    ... 
    c.DocumentFilter<HideTaggedOperationsFilter>(); 
}); 

Poi basta decorare un percorso in questo modo:

[SwaggerTag("MobileOnly")] 
public IHttpActionResult SendTest(Guid userId) 
{ 
    return OK(); 
} 

Edit: ci sono alcuni post emissione nella pagina GitHub per Swashbuckle che consiglia di impostare ognuno dei verbi HTTP su null su swaggerDoc.path in Apply. Ho trovato questo per rompere un sacco di generatori di codice auto come AutoRest, quindi sto semplicemente rimuovendo il percorso nel suo complesso. (Sembra anche più sintetico)

+0

Questo approccio non funziona se si ha lo stesso percorso con diversi verbi, ma si vuole disabilitare solo alcuni. Con GET/trascrizioni e POST/transazioni, entrambi vengono rimossi quando solo un metodo di azione è decorato con l'attributo personalizzato. Dovresti anche prendere in considerazione il metodo http. –

+0

@ParaJaco è un ottimo punto. Probabilmente non sarebbe troppo difficile estenderlo per prendere in considerazione i verbi HTTP. – LukeP

+0

Posso decorare l'intero controller? – Rbacarin