用于包装的 AutoWrapper 响应的 Swagger c# ProduceResponseType
Swagger c# ProduceResponseType for Wrapped AutoWrapper Response
由于我们所有的回复都包装在 AutoWrapper 中,
告诉 swagger 它们被 AutoWrapper 包装的最好方法是什么?
而不是在每个控制器方法上添加它?
[ProducesResponseType( 200, Type = typeof( AutoWrapper<IEnumerable<WeatherForecast>> ) )]
您可以尽量减少您的工作并创建您自己的 API Convention 并将其应用于 Startup.cs 中的程序集。
测试模型:
public class MyTestModel
{
public string SomeString { get; set; }
public int SomeInteger { get; set; }
public bool SomeBool { get; set; }
}
响应包装器(灵感来自 ):
public class AutoWrapper<T>
{
public int StatusCode { get; set; }
public string Message { get; set; }
public T Result { get; set; }
}
约定Class:
public static class MyAppConventions
{
[ProducesResponseType(200, Type = typeof(AutoWrapper<List<MyTestModel>>))]
[ApiConventionNameMatch(ApiConventionNameMatchBehavior.Suffix)]
public static void Weather()
{
}
}
控制器:
[HttpGet]
public List<MyTestModel> GetWeather()
{
List<MyTestModel> models = new List<MyTestModel>
{
new MyTestModel
{
SomeString = "Hello world!",
SomeInteger = 101,
SomeBool = true
}
};
return models;
}
最后,在Startup中,需要添加一个装饰器:
[assembly: ApiConventionType(typeof(MyAppConventions))]
namespace NameSpace
{
public class Startup
{
//stuff...
}
}
在 Swagger 中,这将显示为:
然而,这种方法在某种程度上依赖于跨控制器方法名称建立的命名标准,以最大限度地减少您需要在约定中编写的代码量 class。您可以使用 ApiConventionNameMatch
attribute 来匹配后缀、前缀、任何方法名称(忽略参数)和确切名称(参数或方法必须匹配)。例如,您可以通过在约定 class:
中声明以下内容来覆盖项目中所有名为 Get(int id)
的控制器方法
[ProducesResponseType(200, Type = typeof(AutoWrapper<YourObject>))]
[ApiConventionNameMatch(ApiConventionNameMatchBehavior.Prefix)]
public static void Get(int id)
{
}
这个 Swagger IOperationFilter 对我有用,它为每个操作创建一个 AutoWrapper 类型。
public class ResponseWrapperOperationFilter : IOperationFilter
{
public void Apply ( OpenApiOperation operation, OperationFilterContext context )
{
//eg. IEnumerable<WeatherForecast>
var responseType = context.ApiDescription.SupportedResponseTypes[0].Type;
//eg. AutoWrapper<IEnumerable<WeatherForecast>>
var wrappedResponseType = typeof( AutoWrapperDefinition<> ).MakeGenericType( responseType );
//new schema, TODO is to check if schema exists
var schema = context.SchemaGenerator.GenerateSchema(
wrappedResponseType,
context.SchemaRepository
);
var openApiResponse = new OpenApiResponse
{
Content = new Dictionary<string, OpenApiMediaType>()
{
["application/json"] = new OpenApiMediaType()
{
Schema = schema
}
},
};
operation.Responses.Clear();
operation.Responses.Add( "200", openApiResponse );
//TODO: add Response for other status code
}
}
对 Lydon Ch 回答添加了以下改进:
- 创建前检查现有架构
- 只删除 200 条响应而不是所有响应:
public class ResponseWrapperOperationFilter : IOperationFilter
{
public void Apply ( OpenApiOperation operation, OperationFilterContext context )
{
//eg. IEnumerable<WeatherForecast>
var responseType = context.ApiDescription.SupportedResponseTypes[0].Type;
//eg. AutoWrapper<IEnumerable<WeatherForecast>>
var wrappedResponseType = typeof( AutoWrapperDefinition<> ).MakeGenericType( responseType );
//Added - To Check for existing Schema first
context.SchemaRepository.TryLookupByType(wrappedResponseType, out var schema );
if(schema == null)
{
schema = context.SchemaGenerator.GenerateSchema(
wrappedResponseType,
context.SchemaRepository
);
}
var openApiResponse = new OpenApiResponse
{
Content = new Dictionary<string, OpenApiMediaType>()
{
["application/json"] = new OpenApiMediaType()
{
Schema = schema
}
},
};
operation.Responses.Remove(StatusCodes.Status200OK.ToString());
operation.Responses.Add(StatusCodes.Status200OK.ToString(), openApiResponse);
}
}
由于我们所有的回复都包装在 AutoWrapper 中,
告诉 swagger 它们被 AutoWrapper 包装的最好方法是什么?
而不是在每个控制器方法上添加它?
[ProducesResponseType( 200, Type = typeof( AutoWrapper<IEnumerable<WeatherForecast>> ) )]
您可以尽量减少您的工作并创建您自己的 API Convention 并将其应用于 Startup.cs 中的程序集。
测试模型:
public class MyTestModel
{
public string SomeString { get; set; }
public int SomeInteger { get; set; }
public bool SomeBool { get; set; }
}
响应包装器(灵感来自
public class AutoWrapper<T>
{
public int StatusCode { get; set; }
public string Message { get; set; }
public T Result { get; set; }
}
约定Class:
public static class MyAppConventions
{
[ProducesResponseType(200, Type = typeof(AutoWrapper<List<MyTestModel>>))]
[ApiConventionNameMatch(ApiConventionNameMatchBehavior.Suffix)]
public static void Weather()
{
}
}
控制器:
[HttpGet]
public List<MyTestModel> GetWeather()
{
List<MyTestModel> models = new List<MyTestModel>
{
new MyTestModel
{
SomeString = "Hello world!",
SomeInteger = 101,
SomeBool = true
}
};
return models;
}
最后,在Startup中,需要添加一个装饰器:
[assembly: ApiConventionType(typeof(MyAppConventions))]
namespace NameSpace
{
public class Startup
{
//stuff...
}
}
在 Swagger 中,这将显示为:
然而,这种方法在某种程度上依赖于跨控制器方法名称建立的命名标准,以最大限度地减少您需要在约定中编写的代码量 class。您可以使用 ApiConventionNameMatch
attribute 来匹配后缀、前缀、任何方法名称(忽略参数)和确切名称(参数或方法必须匹配)。例如,您可以通过在约定 class:
Get(int id)
的控制器方法
[ProducesResponseType(200, Type = typeof(AutoWrapper<YourObject>))]
[ApiConventionNameMatch(ApiConventionNameMatchBehavior.Prefix)]
public static void Get(int id)
{
}
这个 Swagger IOperationFilter 对我有用,它为每个操作创建一个 AutoWrapper 类型。
public class ResponseWrapperOperationFilter : IOperationFilter
{
public void Apply ( OpenApiOperation operation, OperationFilterContext context )
{
//eg. IEnumerable<WeatherForecast>
var responseType = context.ApiDescription.SupportedResponseTypes[0].Type;
//eg. AutoWrapper<IEnumerable<WeatherForecast>>
var wrappedResponseType = typeof( AutoWrapperDefinition<> ).MakeGenericType( responseType );
//new schema, TODO is to check if schema exists
var schema = context.SchemaGenerator.GenerateSchema(
wrappedResponseType,
context.SchemaRepository
);
var openApiResponse = new OpenApiResponse
{
Content = new Dictionary<string, OpenApiMediaType>()
{
["application/json"] = new OpenApiMediaType()
{
Schema = schema
}
},
};
operation.Responses.Clear();
operation.Responses.Add( "200", openApiResponse );
//TODO: add Response for other status code
}
}
对 Lydon Ch 回答添加了以下改进:
- 创建前检查现有架构
- 只删除 200 条响应而不是所有响应:
public class ResponseWrapperOperationFilter : IOperationFilter
{
public void Apply ( OpenApiOperation operation, OperationFilterContext context )
{
//eg. IEnumerable<WeatherForecast>
var responseType = context.ApiDescription.SupportedResponseTypes[0].Type;
//eg. AutoWrapper<IEnumerable<WeatherForecast>>
var wrappedResponseType = typeof( AutoWrapperDefinition<> ).MakeGenericType( responseType );
//Added - To Check for existing Schema first
context.SchemaRepository.TryLookupByType(wrappedResponseType, out var schema );
if(schema == null)
{
schema = context.SchemaGenerator.GenerateSchema(
wrappedResponseType,
context.SchemaRepository
);
}
var openApiResponse = new OpenApiResponse
{
Content = new Dictionary<string, OpenApiMediaType>()
{
["application/json"] = new OpenApiMediaType()
{
Schema = schema
}
},
};
operation.Responses.Remove(StatusCodes.Status200OK.ToString());
operation.Responses.Add(StatusCodes.Status200OK.ToString(), openApiResponse);
}
}