用实栗谈一谈Swagger中的SchemaFilter
1. ISchemaFilter 的作用
ISchemaFilter
是一个接口,主要用于拦截并修改自动生成的 Swagger Schema(在 OpenAPI 3.0 中通常指 OpenApiSchema
)的定义。它能够帮助我们在以下场景中使用:
- 为某些属性附加额外的注释或元数据
- 在自动生成的 Schema 中可能会缺少一些与业务相关的描述、或需要为某些属性配置额外的信息(如格式化规则、示例值、只读/只写标记等)。
- 动态地隐藏或修改属性
- 某些属性不希望被公开给第三方使用,或者需要根据特定条件隐藏属性。
- 对一些特殊类型进行处理
- 比如针对
DateTime
、自定义类型等,注入更多描述(format、example、pattern 等)。
- 比如针对
- 对一些复杂类型做深度定制
- 假设你有一个包含子类或泛型集合的复杂对象,需要对子类型也进行特别处理时,就可以在此扩展。
简而言之,ISchemaFilter
能让我们在生成 Swagger 文档时,对每一个类型的 Schema 进行细粒度的定制。
2. 举个栗子
接下来,我们结合实际的使用场景举一个小栗子:
在一个前后端分离的项目中,后端的ID是用雪花算法生成的long类型,长度比较长,可能会超过了前端的Number类型的最大长度,有可能会导致前端丢失精度的问题。解决方案也很简单,只要Json序列化的时候,将long和string进行转换,前端用string类型,后端还是long类型。
- 第一步: 写一个JsonConverter,这里就不赘述了,反正就是Json序列化的时候,Read的时候返回long,Write的时候写成string(如果不用Swagger的话,这里就结束了~);
- 第二步: 定义一个SnowflakeIdAttribute, 用来标注那些是雪花ID。当然了,这里也可以顺带做一点验证;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class SnowflakeIdAttribute : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext? validationContext)
{
var displayName = validationContext?.DisplayName ?? "该字段";
var memberName = validationContext?.MemberName ?? displayName;
return value switch
{
null or long or IEnumerable<long> => ValidationResult.Success,
string strValue => long.TryParse(strValue, out _)
? ValidationResult.Success
: new ValidationResult($"{displayName} 必须是可转换为 long 类型的字符串。", [memberName]),
IEnumerable<string> strArray => strArray.Any(str => !long.TryParse(str, out _))
? new ValidationResult($"{displayName} 中的所有字符串必须是可转换为 long 类型。", [memberName])
: ValidationResult.Success,
_ => new ValidationResult($"{displayName} 必须是 string 类型或 long 类型。", [memberName])
};
}
}
- 第三步: 定义一个SnowflakeIdSchemaFilter, 集成ISchemaFilter, 实现Swagger中string和long的转换;
public class SnowflakeIdSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
foreach (var property in context.Type.GetProperties())
{
var attrs = property.GetCustomAttributes(typeof(SnowflakeIdAttribute), true);
if (attrs.Any())
{
// Convert property name to camel case
var propertyName = Utils.ToCamelCase(property.Name);
if (schema.Properties.ContainsKey(propertyName))
{
var propSchema = schema.Properties[propertyName];
if (IsEnumerableOfLong(property.PropertyType))
{
// If the property is IEnumerable<long>, change the item type to IENumerable<string>
propSchema.Type = "array";
propSchema.Items = new OpenApiSchema
{
Type = "string",
Example = new OpenApiString("snowflakeId")
}; }
else if (property.PropertyType == typeof(long) || property.PropertyType == typeof(long?))
{
// For single long type properties
propSchema.Type = "string";
propSchema.Example = new OpenApiString("snowflakeId");
}
}
}
}
}
private static bool IsEnumerableOfLong(Type type)
{
if (type.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(type.GetGenericTypeDefinition()))
{
var itemType = type.GetGenericArguments()[0];
return itemType == typeof(long) || itemType == typeof(long?);
}
return false;
}
}
- 第四步: 注册一下这个过滤器:
builder.Services.AddSwaggerGen(options =>
{
options.SchemaFilter<SnowflakeIdSchemaFilter>();
}
- 第五步: 验证一下
public class DemoDto
{
/// <summary>
/// 雪花ID
/// </summary>
[Required]
[SnowflakeId]
[JsonConverter(typeof(SnowflakeIdConverter))]
public long? Id { get; set; }
/// <summary>
/// 雪花ID列表
/// </summary>
[SnowflakeId]
[JsonConverter(typeof(SnowflakeIdsConverter))]
public IEnumerable<long> Ids { get; set; } = [];
}
testApi.MapPost("/test",
[SwaggerOperation(Summary = "测试例子")] (DemoDto dto) =>
$"from body: IdStr: {dto.Id}, IdStrList: {string.Join(',', dto.Ids ?? [])}");
3. 小结
- ISchemaFilter 接口主要用于在生成 Swagger 文档时,自定义或修改自动生成的 Schema(即每个模型/实体的定义)。
- 可以应用在多种场景中:为属性添加描述、示例,隐藏属性,为特定类型添加或修改自定义信息等。
- 只需要在自己的项目中实现
ISchemaFilter
接口,并在AddSwaggerGen
时通过c.SchemaFilter<T>()
进行注册即可。
通过灵活地使用 ISchemaFilter
,我们可以使 Swagger 文档更加清晰、准确地反映业务需求,为前后端或第三方对接带来更好的体验。
posted on 2024-12-24 15:42 God写注释没有代码 阅读(45) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)