2015-12-21 25 views
6

Ho un progetto Web API 5.2 Asp.Net in C# e la documentazione di generazione con Swashbuckle.Come posso includere sottoclassi nella documentazione dell'API Swagger usando Swashbuckle?

Ho un modello che contiene ereditarietà qualcosa come avere una proprietà animale da una classe astratta animale e classi di cani e gatti che ne derivano.

Swashbuckle mostra solo lo schema per la classe Animal, quindi ho provato a giocare con ISchemaFilter (che cosa suggeriscono anch'io) ma non sono riuscito a farlo funzionare e inoltre non riesco a trovare un esempio corretto.

Chiunque può aiutare?

+0

Qualsiasi fortuna capire questo fuori? – Craig

+0

Non ancora, ma dovrò esaminarlo di nuovo presto. Per favore fatemi sapere se trovate qualcosa –

risposta

13

Sembra che lo Swashbuckle non implementi correttamente il polimorfismo e capisco il punto di vista dell'autore sulle sottoclassi come parametri (se un'azione si aspetta una classe Animale e si comporta in modo diverso se la si chiama con un oggetto cane o un oggetto gatto , quindi dovresti avere 2 azioni diverse ..) ma come tipi di ritorno credo che sia corretto restituire Animal e gli oggetti potrebbero essere di tipo Cane o Gatto.

Quindi, per descrivere la mia API e produrre uno schema JSON adeguato in linea con le linee guida corrette (essere a conoscenza del modo in cui descrivo il disciminatore, se si dispone del proprio discriminatore potrebbe essere necessario modificare quella parte in particolare), io uso filtri di documenti e di schema come segue:.

SwaggerDocsConfig configuration; 
..... 
configuration.DocumentFilter<PolymorphismDocumentFilter<YourBaseClass>>(); 
configuration.SchemaFilter<PolymorphismSchemaFilter<YourBaseClass>>(); 
..... 

public class PolymorphismSchemaFilter<T> : ISchemaFilter 
{ 
    private readonly Lazy<HashSet<Type>> derivedTypes = new Lazy<HashSet<Type>>(Init); 

    private static HashSet<Type> Init() 
    { 
     var abstractType = typeof(T); 
     var dTypes = abstractType.Assembly 
           .GetTypes() 
           .Where(x => abstractType != x && abstractType.IsAssignableFrom(x)); 

     var result = new HashSet<Type>(); 

     foreach (var item in dTypes) 
      result.Add(item); 

     return result; 
    } 

    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type) 
    { 
     if (!derivedTypes.Value.Contains(type)) return; 

     var clonedSchema = new Schema 
           { 
            properties = schema.properties, 
            type = schema.type, 
            required = schema.required 
           }; 

     //schemaRegistry.Definitions[typeof(T).Name]; does not work correctly in SwashBuckle 
     var parentSchema = new Schema { @ref = "#/definitions/" + typeof(T).Name }; 

     schema.allOf = new List<Schema> { parentSchema, clonedSchema }; 

     //reset properties for they are included in allOf, should be null but code does not handle it 
     schema.properties = new Dictionary<string, Schema>(); 
    } 
} 

public class PolymorphismDocumentFilter<T> : IDocumentFilter 
{ 
    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, System.Web.Http.Description.IApiExplorer apiExplorer) 
    { 
     RegisterSubClasses(schemaRegistry, typeof(T)); 
    } 

    private static void RegisterSubClasses(SchemaRegistry schemaRegistry, Type abstractType) 
    { 
     const string discriminatorName = "discriminator"; 

     var parentSchema = schemaRegistry.Definitions[SchemaIdProvider.GetSchemaId(abstractType)]; 

     //set up a discriminator property (it must be required) 
     parentSchema.discriminator = discriminatorName; 
     parentSchema.required = new List<string> { discriminatorName }; 

     if (!parentSchema.properties.ContainsKey(discriminatorName)) 
      parentSchema.properties.Add(discriminatorName, new Schema { type = "string" }); 

     //register all subclasses 
     var derivedTypes = abstractType.Assembly 
             .GetTypes() 
             .Where(x => abstractType != x && abstractType.IsAssignableFrom(x)); 

     foreach (var item in derivedTypes) 
      schemaRegistry.GetOrRegister(item); 
    } 
} 

Che il codice precedente implementa è specificato here, nella sezione "Modelli con polimorfismo supporto produce fondamentalmente qualcosa di simile al seguente:

{ 
    "definitions": { 
    "Pet": { 
     "type": "object", 
     "discriminator": "petType", 
     "properties": { 
     "name": { 
      "type": "string" 
     }, 
     "petType": { 
      "type": "string" 
     } 
     }, 
     "required": [ 
     "name", 
     "petType" 
     ] 
    }, 
    "Cat": { 
     "description": "A representation of a cat", 
     "allOf": [ 
     { 
      "$ref": "#/definitions/Pet" 
     }, 
     { 
      "type": "object", 
      "properties": { 
      "huntingSkill": { 
       "type": "string", 
       "description": "The measured skill for hunting", 
       "default": "lazy", 
       "enum": [ 
       "clueless", 
       "lazy", 
       "adventurous", 
       "aggressive" 
       ] 
      } 
      }, 
      "required": [ 
      "huntingSkill" 
      ] 
     } 
     ] 
    }, 
    "Dog": { 
     "description": "A representation of a dog", 
     "allOf": [ 
     { 
      "$ref": "#/definitions/Pet" 
     }, 
     { 
      "type": "object", 
      "properties": { 
      "packSize": { 
       "type": "integer", 
       "format": "int32", 
       "description": "the size of the pack the dog is from", 
       "default": 0, 
       "minimum": 0 
      } 
      }, 
      "required": [ 
      "packSize" 
      ] 
     } 
     ] 
    } 
    } 
} 
+2

'SchemaIdProvider' deve essere la vostra classe? Ho capito che puoi usare la convenzione di default di Swagger aggiungendo un 'Using Swashbuckle.Swagger' e poi cambiando quella riga di codice in' var parentSchema = schemaRegistry.Definitions [abstractType.FriendlyId]; ' – wags1999

+0

Sì, è la mia classe. Ne ho bisogno perché abbiamo anche un delegato per schemaId: configuration.SchemaId (SchemaIdProvider.GetSchemaId); –

+2

@PaoloVigori: l'ho usato su Swashbuckle.AspNetCore, il 'PolymorphismDocumentFilter' è chiamato e il discriminatore è impostato nel codice, ma non nella definizione di swagger generata. le voci 'allOf' ci sono. Qualche idea? – Tseng