如何告诉Swashbuckle请求体内容是必需的?

17

我有一个WebAPI控制器,它接受二进制包并将它们存储在某个地方。由于这些包可能变得非常大,我不想通过添加字节数组参数来将它们加载到内存中,而是传递流。

我在这个答案中找到了一种方法:

[HttpPost]
[Route("Store/{projectId}")]
public async Task Store(string projectId)
{
    using (var stream = await this.Request.Content.ReadAsStreamAsync())
    {
        await this.packageManager.StorePackageAsync(projectId, stream);
    }
}

这个可以用Postman发送文件到控制器,现在我想用Swashbuckle生成Swagger文档,但是需要的body内容没有提到。

有没有办法获取请求内容的流,以便于Swashbuckle知道它呢?或者有没有属性可以告诉它所需的内容?

4个回答

19

又来更新了。这里是我使用 ASP.NET Core 3.1Swashbuckle.AspNetCore.Swagger 5.0.0 得出的解决方案:

public class BinaryContentAttribute : Attribute
{
}
public class BinaryContentFilter : IOperationFilter
{
    /// <summary>
    /// Configures operations decorated with the <see cref="BinaryContentAttribute" />.
    /// </summary>
    /// <param name="operation">The operation.</param>
    /// <param name="context">The context.</param>
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var attribute = context.MethodInfo.GetCustomAttributes(typeof(BinaryContentAttribute), false).FirstOrDefault();
        if (attribute == null)
        {
            return;
        }

        operation.RequestBody = new OpenApiRequestBody() { Required = true };
        operation.RequestBody.Content.Add("application/octet-stream", new OpenApiMediaType()
        {
            Schema = new OpenApiSchema()
            {
                Type = "string",
                Format = "binary",
            },
        });
    }
}

Startup.cs 中的 ConfigureServices

services.AddSwaggerGen(o =>
{
    o.OperationFilter<BinaryContentFilter>();
});

1
我该如何做到这一点,同时让Swagger知道所定义的请求体类型? - Ryan Langton
1
@RyanLangton 在控制器方法中添加[BinaryContent]属性。 - Greg

14
为了实现这一点,您需要完成几个步骤。
首先,您需要告诉Swagger在请求body中包含二进制数据的参数。接下来,您需要告诉Swagger终端点使用二进制数据(例如application/octet-stream)。
Swashbuckle默认不支持此功能。但是您可以创建自定义过滤器来扩展Swashbuckle的功能。我通常的做法是创建一个自定义属性来装饰方法,然后创建一个自定义过滤器来处理该属性。
针对您的情况,以下内容将解决问题:
自定义属性:
public class BinaryPayloadAttribute : Attribute
{
    public BinaryPayloadAttribute()
    {
        ParameterName = "payload";
        Required = true;
        MediaType = "application/octet-stream";
        Format = "binary";
    }

    public string Format { get; set; }

    public string MediaType { get; set; }

    public bool Required { get; set; }

    public string ParameterName { get; set; }
}

自定义过滤器

public class BinaryPayloadFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var attribute = apiDescription.GetControllerAndActionAttributes<BinaryPayloadAttribute>().FirstOrDefault();
        if (attribute == null)
        {
            return;
        }

        operation.consumes.Clear();
        operation.consumes.Add(attribute.MediaType);

        operation.parameters.Add(new Parameter
        {
            name = attribute.ParameterName,
            @in = "body", 
            required = attribute.Required,
            type = "string", 
            format = attribute.Format
        });
    }
}

将过滤器添加到Swashbuckle配置中

GlobalConfiguration.Configuration 
    .EnableSwagger(c => 
        {
            // other configuration setting removed for brevity
            c.OperationFilter<BinaryPayloadFilter>();
        });

将属性应用于您的方法

[HttpPost]
[BinaryPayload]
[Route("Store/{projectId}")]
public async Task Store(string projectId)
{
    ...
}

在Swagger UI中,您将获得以下内容:

Swagger UI


请注意保留HTML标记。

1
BinaryPayloadAttribute 的参数中,我们可以添加一个描述吗? - Dot Net Dev

2
在Swashbuckle 4.0中,语法略有变化:

最初的回答

public class BinaryPayloadAttribute : Attribute
{
    public BinaryPayloadAttribute()
    {
        ParameterName = "payload";
        Required = true;
        MediaType = "application/octet-stream";
        Format = "binary";
    }

    public string Format { get; set; }
    public string MediaType { get; set; }
    public bool Required { get; set; }
    public string ParameterName { get; set; }
}

public class BinaryPayloadFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        var attribute = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
            .Union(context.MethodInfo.GetCustomAttributes(true))
            .OfType<BinaryPayloadAttribute>().FirstOrDefault();
        if (attribute == null)
        {
            return;
        }

        operation.Consumes.Clear();
        operation.Consumes.Add(attribute.MediaType);

        operation.Parameters.Add(new BodyParameter
        {
            Name = attribute.ParameterName,
            @In = "body",
            Required = attribute.Required,                                
            Schema = new Schema
            {
                Type = "string",
                Format = attribute.Format
            }
        });
    }
}

And:

services.AddSwaggerGen(c =>
{
    c.OperationFilter<BinaryPayloadFilter>();
});

0
这是@venerik答案的更新版本。这适用于Swashbuckle 2.3.0:
自定义属性:
/// <summary>
/// Represents controller actions that accept a binary payload.
/// </summary>
public class BinaryPayloadAttribute : Attribute
{
    /// <summary>
    /// Initializes a new instance of the <see cref="BinaryPayloadAttribute"/> class.
    /// </summary>
    public BinaryPayloadAttribute()
    {
        ParameterName = "payload";
        Required = true;
        MediaType = "application/octet-stream";
        Format = "binary";
    }

    /// <summary>
    /// Gets or sets the payload format.
    /// </summary>
    public string Format { get; set; }

    /// <summary>
    /// Gets or sets the payload media type.
    /// </summary>
    public string MediaType { get; set; }

    /// <summary>
    /// Gets or sets a required flag.
    /// </summary>
    public bool Required { get; set; }

    /// <summary>
    /// Gets or sets a parameter name.
    /// </summary>
    public string ParameterName { get; set; }
}

自定义过滤器:

/// <summary>
/// Filter for a controller action that accept a binary payload.
/// </summary>
public class BinaryPayloadFilter : IOperationFilter
{
    /// <summary>
    /// Applies the specified operation.
    /// </summary>
    /// <param name="operation">The operation.</param>
    /// <param name="context">The context.</param>
    public void Apply(Operation operation, OperationFilterContext context)
    {
        BinaryPayloadAttribute attribute = context.ApiDescription.ActionAttributes().FirstOrDefault(x => x is BinaryPayloadAttribute) as BinaryPayloadAttribute;
        if (attribute == null)
        {
            return;
        }

        operation.Consumes.Clear();
        operation.Consumes.Add(attribute.MediaType);

        operation.Parameters.Add(new BodyParameter
        {
            Name = attribute.ParameterName,
            Required = attribute.Required
        });
    }
}

将过滤器添加到Swashbuckle配置中:

GlobalConfiguration.Configuration 
    .EnableSwagger(c => 
        {
            // other configuration setting removed for brevity
            c.OperationFilter<BinaryPayloadFilter>();
        });

将属性应用于您的方法:

[HttpPost]
[BinaryPayload]
[Route("Store/{projectId}")]
public async Task Store(string projectId)
{
    ...
}

你两次发布了筛选器 :-) 有可能看到属性吗? - CJe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接