【问题标题】:How can I tell Swashbuckle that the body content is required?我如何告诉 Swashbuckle 需要正文内容?
【发布时间】:2016-12-14 11:10:51
【问题描述】:

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

我找到了一种方法in this answer

[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 文档,当然,这里没有提到所需的正文内容。

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

【问题讨论】:

    标签: asp.net-web-api httprequest swagger-2.0 swashbuckle


    【解决方案1】:

    要实现这一点,您必须做几件事。

    首先您必须告诉 Swagger 在正文中有一个包含二进制数据的参数。接下来你必须告诉 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 中你会得到:

    【讨论】:

    • 可以给BinaryPayloadAttribute中的参数添加描述吗?
    【解决方案2】:

    又一次更新。这是我最终使用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",
                },
            });
        }
    }
    
    

    ConfigureServices in Startup.cs:

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

    【讨论】:

    • 如何才能做到这一点,但要让 swagger 知道 body 的定义类型?
    【解决方案3】:

    在 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
                }
            });
        }
    }
    

    还有:

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

    【讨论】:

      【解决方案4】:

      这是@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)
      {
          ...
      }
      

      【讨论】:

      • 您发布了两次过滤器 :-) 有机会看到该属性吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-20
      • 2014-07-02
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      相关资源
      最近更新 更多