ASP.netコアで Swashbuckle を使用しています。それは下部にモデルのリストを備えたニースのウェブサイトを作成しています。
まだ表示されていないモデルをこのリストに追加するにはどうすればよいですか?
私はリクエストの1つで抽象クラスを返し、その抽象クラスを継承するすべてのバリエーションをリストしたいと思います。
前もって感謝します
ドキュメントフィルターを作成して、グローバルに登録できます。
public class CustomModelDocumentFilter<T> : IDocumentFilter where T : class
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
context.SchemaRegistry.GetOrRegister(typeof(T));
}
}
それをStartup
クラスに登録します。
services.AddSwaggerGen(options =>
{
...
options.DocumentFilter<CustomModelDocumentFilter<MyCustomModel>>();
options.DocumentFilter<CustomModelDocumentFilter<MyOtherModel>>();
...
}
ポリモーフィッククラスの場合、これらをフィルターに使用できます( この答え のわずかに改良されたバージョン)。
public class PolymorphismDocumentFilter<T> : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
RegisterSubClasses(context.SchemaRegistry, typeof(T));
}
private static void RegisterSubClasses(ISchemaRegistry schemaRegistry, Type abstractType)
{
const string discriminatorName = "$type";
string friendlyId = abstractType.FriendlyId();
if (!schemaRegistry.Definitions.TryGetValue(friendlyId, out Schema parentSchema))
parentSchema = schemaRegistry.GetOrRegister(abstractType);
// set up a discriminator property (it must be required)
parentSchema.Discriminator = discriminatorName;
parentSchema.Required = new List<string> { discriminatorName };
if (parentSchema.Properties == null)
parentSchema.Properties = new Dictionary<string, Schema>();
if (!parentSchema.Properties.ContainsKey(discriminatorName))
parentSchema.Properties.Add(discriminatorName, new Schema { Type = "string", Default = abstractType.FullName });
// register all subclasses
var derivedTypes = abstractType.GetTypeInfo().Assembly.GetTypes()
.Where(x => abstractType != x && abstractType.IsAssignableFrom(x));
foreach (var item in derivedTypes)
schemaRegistry.GetOrRegister(item);
}
}
public class PolymorphismSchemaFilter<T> : ISchemaFilter
{
private readonly Lazy<HashSet<Type>> derivedTypes = new Lazy<HashSet<Type>>(Init);
public void Apply(Schema schema, SchemaFilterContext context)
{
if (!derivedTypes.Value.Contains(context.SystemType)) return;
var type = context.SystemType;
var clonedSchema = new Schema
{
Properties = schema.Properties,
Type = schema.Type,
Required = schema.Required
};
// schemaRegistry.Definitions[typeof(T).Name]; does not work correctly in Swashbuckle.AspNetCore
var parentSchema = new Schema { Ref = "#/definitions/" + typeof(T).Name };
var assemblyName = Assembly.GetAssembly(type).GetName();
schema.Discriminator = "$type";
// This is required if you use Microsoft's AutoRest client to generate the JavaScript/TypeScript models
schema.Extensions.Add("x-ms-discriminator-value", $"{type.FullName}, {assemblyName.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>();
}
private static HashSet<Type> Init()
{
var abstractType = typeof(T);
var dTypes = abstractType.GetTypeInfo().Assembly
.GetTypes()
.Where(x => abstractType != x && abstractType.IsAssignableFrom(x));
var result = new HashSet<Type>();
foreach (var item in dTypes)
result.Add(item);
return result;
}
}
2つのフィルターが必要です。 1つ目は、提供されたすべてのクラスをスキーマに追加します。また、基本クラスに存在しないプロパティを派生型のスキーマに追加します。
2番目のフィルターはいくつかのプロパティ($type
(モデルが返されたときのシリアル化)と拡張機能(MicrosoftのAutoRestクライアント/ジェネレーターの場合)、およびSwaggerスキーマにallOf
プロパティを追加します。これらは、swaggerで生成されたときに継承スキーマを作成するために必要です-genまたはAutoRest。
登録は似ていますが、ペアで登録する必要があるだけです(基本クラスの登録のみが必要です)。
// The following lines add polymorphism to the swagger.json schema, so that
// code generators can create properly inheritance hierarchies.
options.DocumentFilter<PolymorphismDocumentFilter<BaseClass>>();
options.SchemaFilter<PolymorphismSchemaFilter<BaseClass>>();
多分最もクリーンなソリューションではないかもしれませんが、コントローラーの上にProducesResponseType
属性を設定することで同じことを達成しました:
[ProducesResponseType(typeof(object), 200)]
public class FileController : Controller
{
オブジェクトをモデルに表示するオブジェクトに置き換え、追加するそれぞれに新しい線を作成します。必ず、それぞれに異なるステータスコードを使用してください。そうしないと、最後のステータスコードしか表示されません。
関数の戻り値の型が抽象型だったため、モデルがswaggerで表示されないという同じ問題がありました。上記の答えを変更して、名前空間からモデルリストにすべてをダンプできるようにしました。
スタートアップでは、この関数を定義します。
public class GenericAPI_DocumentFilter<T> : IDocumentFilter where T : class
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var t in Assembly.GetExecutingAssembly().GetTypes())
{
if (t.Namespace.Contains("MyAPI") && t.IsClass)
{
var a = t.GetCustomAttribute(typeof(DataContractAttribute));
if (a != null)
{
context.SchemaRegistry.GetOrRegister(t);
}
}
}
}
}
Swaggerの初期化では、1行追加します。
services.AddSwaggerGen(opt =>
{
...
opt.DocumentFilter<GenericAPI_DocumentFilter<object>>();
...
}
振り返ってみると、以下(および他のページ)で私が見つけたもう1つの答えは、この種の属性を追加することでした。
[HttpGet("")]
[ProducesResponseType(typeof(MyResult), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(ErrorBase), (int)System.Net.HttpStatusCode.NotFound)]
... function definition ...
理由:関数はさまざまな状況に基づいてさまざまなタイプのオブジェクトを提供するため、これらの属性を使用して、どのオブジェクトをどの状況で返すかを指定できます。
例えば私は返すことができます
return Ok(myresult)
または
return NotFound(myerror)
結果を見つけたかどうかに基づいて私の機能で。