用于包装的 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);
    }
}