我如何告诉 Swashbuckle 正文内容是必需的?
How can I tell Swashbuckle that the body content is required?
我有一个 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 知道它?或者有没有我可以用来告诉它所需内容的属性?
要实现这一点,您必须做几件事。
首先你必须告诉 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 中你会得到:
这是@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)
{
...
}
在 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>();
});
又一次更新。这是我最终使用 ASP.NET Core 3.1
和 Swashbuckle.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
在 Startup.cs
:
services.AddSwaggerGen(o =>
{
o.OperationFilter<BinaryContentFilter>();
});
我有一个 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 知道它?或者有没有我可以用来告诉它所需内容的属性?
要实现这一点,您必须做几件事。
首先你必须告诉 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 中你会得到:
这是@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)
{
...
}
在 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>();
});
又一次更新。这是我最终使用 ASP.NET Core 3.1
和 Swashbuckle.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
在 Startup.cs
:
services.AddSwaggerGen(o =>
{
o.OperationFilter<BinaryContentFilter>();
});