.net swagger知识点汇总 自定义swagger返回值
注入
services.AddSwaggerGen(options =>
{
//使用options注入服务
});
SwaggerDoc
SwaggerDoc主要用来声明一个文档,上面的例子中声明了一个名称为v1的接口文档,可声明多个接口文档,比如按开发版本进行声明:
options.SwaggerDoc("v1", new OpenApiInfo()
{
Version = "v0.0.1",
Title = "项目v0.0.1",
Description = $"接口文档说明v0.0.1",
Contact = new OpenApiContact()
{
Name = "zhangsan",
Email = "xxx@qq.com",
Url = null
}
});
options.SwaggerDoc("v2", new OpenApiInfo()
{
Version = "v0.0.2",
Title = "项目v0.0.2",
Description = $"接口文档说明v0.0.2",
Contact = new OpenApiContact()
{
Name = "lisi",
Email = "xxxx@qq.com",
Url = null
}
});
IncludeXmlComments
IncludeXmlComments是用于加载注释文件,Swashbuckle会从注释文件中去获取接口的注解,接口参数说明以及接口返回的参数说明等信息,这个在上面的一般用法中已经介绍了,这里不再重复说明
IgnoreObsoleteActions
IgnoreObsoleteActions表示过滤掉ObsoleteAttribute属性声明的接口,也就是说不会在SwaggerUI中显示接口了,ObsoleteAttribute修饰的接口表示接口已过期,尽可能不要再使用。
方法调用等价于:options.SwaggerGeneratorOptions.IgnoreObsoleteActions = true;
IgnoreObsoleteProperties
IgnoreObsoleteProperties的作用类似于IgnoreObsoleteActions,只不过IgnoreObsoleteActions是作用于接口,而IgnoreObsoleteProperties作用于接口的请求实体和响应实体参数中的属性。
方法调用等价于:
options.SchemaGeneratorOptions.IgnoreObsoleteProperties = true;
OrderActionsBy
OrderActionsBy用于同一组接口(可以理解为同一控制器下的接口)的排序,默认情况下,一般都是按接口所在类的位置进行排序(源码中是按控制器名称排序,但是同一个控制器中的接口是一样的)。
比如上面的例子中,我们可以修改成按接口路由长度排序:
options.OrderActionsBy(apiDescription => apiDescription.RelativePath.Length.ToString());
CustomSchemaIds
CustomSchemaIds方法用于自定义SchemaId,Swashbuckle中的每个Schema都有唯一的Id,框架会使用这个Id匹配引用类型,因此这个Id不能重复。
默认情况下,这个Id是根据类名得到的(不包含命名空间),因此,当我们有两个相同名称的类时,Swashbuckle就会报错:
System.InvalidOperationException: Can't use schemaId "$XXXXX" for type "$XXXX.XXXX". The same schemaId is already used for type "$XXXX.XXXX.XXXX"
就是类似上面的异常,一般时候我们都得去改类名,有点不爽,这时就可以使用这个方法自己自定义实现SchemaId的获取,比如,我们自定义实现使用类名的全限定名(包含命名空间)来生成SchemaId,上面的异常就没有了:
options.CustomSchemaIds(CustomSchemaIdSelector);
string CustomSchemaIdSelector(Type modelType)
{
if (!modelType.IsConstructedGenericType) return modelType.FullName.Replace("[]", "Array");
var prefix = modelType.GetGenericArguments()
.Select(genericArg => CustomSchemaIdSelector(genericArg))
.Aggregate((previous, current) => previous + current);
return prefix + modelType.FullName.Split('`').First();
}
TagActionsBy
Tag是标签组,也就是将接口做分类的一个概念。
TagActionsBy用于获取一个接口所在的标签分组,默认的接口标签分组是控制器名,也就是接口被分在它所属的控制器下面,我们可以改成按请求方法进行分组
options.TagActionsBy(apiDescription => new string[] { apiDescription.HttpMethod});
AddSecurityDefinition
AddSecurityDefinition用于声明一个安全认证,注意,只是声明,并未指定接口必须要使用认证,比如声明JwtBearer认证方式:
//定义JwtBearer认证方式一
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
{
Description = "这是方式一(直接在输入框中输入认证信息,不需要在开头添加Bearer)",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
AddSecurityRequirement
AddSecurityDefinition仅仅是声明已一个认证,不一定要对接口用,而AddSecurityRequirement是将声明的认证作用于所有接口(AddSecurityRequirement好像可以声明和引用一起实现),比如将上面的JwtBearer认证作用于所有接口:
//声明一个Scheme,注意下面的Id要和上面AddSecurityDefinition中的参数name一致
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
//注册全局认证(所有的接口都可以使用认证)
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
});
运行后,发现所有接口后面多了一个锁,表明此接口需要认证信息:
DocumentFilter
DocumentFilter是文档过滤器,它是在获取swagger文档接口,返回结果前调用,也就是请求swagger.json时调用,它允许我们对即将返回的swagger文档信息做调整,比如上面的例子中添加的全局认证方式和AddSecurityRequirement添加的效果是一样的:
public class MyDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
//声明一个Scheme,注意下面的Id要和上面AddSecurityDefinition中的参数name一致
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
//注册全局认证(所有的接口都可以使用认证)
swaggerDoc.SecurityRequirements.Add(new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
});
}
}
然后使用DocumentFilter方法添加过滤器:
options.DocumentFilter<MyDocumentFilter>();
DocumentFilter方法需要提供一个实现了IDocumentFilter接口的Apply方法的类型和它实例化时所需要的的参数,而IDocumentFilter的Apply方法提供了OpenApiDocument和DocumentFilterContext两个参数,DocumentFilterContext参数则包含了当前文件接口方法的信息,比如调用的接口的Action方法和Action的描述(如路由等)。而OpenApiDocument即包含当前请求的接口文档信息,它包含的属性全部都是全局性的, 这样我们可以像上面添加认证一样去添加全局配置,比如,如果不使用AddServer方法,我们可以使用DocumentFilter去添加:
public class MyDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Servers.Add(new OpenApiServer() { Url = "http://localhost:90", Description = "地址1" });
swaggerDoc.Servers.Add(new OpenApiServer() { Url = "http://127.0.0.1:90", Description = "地址2" });
//192.168.28.213是我本地IP
swaggerDoc.Servers.Add(new OpenApiServer() { Url = "http://192.168.28.213:90", Description = "地址3" });
}
}
再比如,上面我们对接口进行了swagger文档分类使用的是ApiExplorerSettingsAttribute,如果不想对每个接口使用ApiExplorerSettingsAttribute,我们可以使用DocumentFilter来实现,先创建一个类实现IDocumentFilter接口:
public class GroupNameDocumentFilter : IDocumentFilter
{
string documentName;
string[] actions;
public GroupNameDocumentFilter(string documentName, params string[] actions)
{
this.documentName = documentName;
this.actions = actions;
}
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var apiDescription in context.ApiDescriptions)
{
if (actions.Contains(apiDescription.ActionDescriptor.RouteValues["action"]))
{
apiDescription.GroupName = documentName;
}
}
}
}
//然后使用DocumentFilter添加过滤器:
//All和Get接口属于文档v1
options.DocumentFilter<GroupNameDocumentFilter>(new object[] { "v1", new string[] { nameof(HomeController.Get) } });
//All和Post接口属于v2
options.DocumentFilter<GroupNameDocumentFilter>(new object[] { "v2", new string[] { nameof(HomeController.Post) } });
OperationFilter
Operation可以简单的理解为一个操作,因为swagger是根据项目中的接口,自动生成接口文档,就自然需要对每个接口进行解析,接口路由是什么,接口需要什么参数,接口返回什么数据等等,而对每个接口的解析就可以视为一个Operation。
OperationFilter是操作过滤器,这个方法需要一个实现类IOperationFilter接口的类型,而它的第二个参数arguments是这个类型实例化时传入的参数。
OperationFilter允许我们对已经生成的接口进行修改,比如可以添加参数,修改参数类型等等。
需要注意的是,OperationFilter在获取swagger文档接口时调用,也就是请求swagger.json时调用,而且只对属于当前请求接口文档的接口进行过滤调用。
比如我们有一个Operation过滤器:
public class MyOperationFilter : IOperationFilter
{
string documentName;
public MyOperationFilter(string documentName)
{
this.documentName = documentName;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
//过滤处理
}
}
接着调用SwaggerGenOptions的OperationFilter方法添加
options.OperationFilter<MyOperationFilter>(new object[] { "v1" });
上面的过滤器实例化需要一个参数documentName,所以在OperationFilter方法中有一个参数。
这个接口只会对当前请求的接口文档进行调用,也就是说,如果我们请求的是swagger文档v1,也就是请求/swagger/v1/swagger.json时,这个过滤器会对All方法和Get方法执行,如果请求的是swagger文档v2,也就是请求/swagger/v2/swagger.json时,这个过滤器会对All方法和Post方法进行调用。自定义的OperationFilter需要实现IOperationFilter的Apply接口方法,而Apply方法有两个参数:OpenApiOperation和OperationFilterContext,同样的,OpenApiOperation包含了和当前接口相关的信息,比如认证情况,所属的标签,还可以自定义的自己的Servers。而OperationFilterContext则包换了接口方法的的相关引用。
OperationFilter是用的比较多的方法了,比如上面的全局认证,因为直接调用AddSecurityRequirement添加的是全局认证,但是项目中可能部分接口不需要认证,这时我们就可以写一个OperationFilter对每一个接口进行判断了:
public class ResponsesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
var list = new List<OpenApiSecurityRequirement>();
if (authAttributes.Any() && !context.MethodInfo.GetCustomAttributes(true).OfType<AllowAnonymousAttribute>().Any())
{
operation.Responses["401"] = new OpenApiResponse { Description = "Unauthorized" };
//operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
//声明一个Scheme,注意下面的Id要和AddSecurityDefinition中的参数name一致
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
//注册全局认证(所有的接口都可以使用认证)
operation.Security = new List<OpenApiSecurityRequirement>(){new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
}};
}
}
}
然后使用OperationFilter添加这个过滤器:
options.OperationFilter<ResponsesOperationFilter>();
swagger统一返回值
public class MyOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
foreach (var key in operation.Responses.Keys)
{
var content = operation.Responses[key].Content;
foreach (var mediaTypeKey in content.Keys)
{
var mediaType = content[mediaTypeKey];
var schema = new OpenApiSchema();
schema.Type = "object";
schema.Properties = new Dictionary<string, OpenApiSchema>()
{
["code"] = new OpenApiSchema() { Type = "integer" },
["message"] = new OpenApiSchema() { Type = "string" },
["error"] = new OpenApiSchema()
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>()
{
["message"] = new OpenApiSchema() { Type = "string" },
["stackTrace"] = new OpenApiSchema() { Type = "string" }
}
},
["result"] = mediaType.Schema
};
mediaType.Schema = schema;
}
}
}
}
注入
options.OperationFilter
RequestBodyFilter
RequestBody理所当然的就是请求体了,一般指的就是Post请求,RequestBodyFilter就是允许我们对请求体的信息作出调整,同样的,它是在获取Swagger.json文档时调用,而且只对那些有请求体的接口才会执行。
RequestBodyFilter的用法类似DocumentFilter和OperationFilter,一般也不会去修改请求体的默认行为,因为它可能导致请求失败,所以一般不常用,这里就不介绍了
ParameterFilter
Parameter指的是接口的参数,而ParameterFilter当然就是允许我们对参数的结构信息作出调整了,同样的,它是在获取Swagger.json文档时调用,而且只对那些参数的接口才会执行。
SchemaFilter
Schema指的是结构,一般指的是接口请求参数和响应返回的参数结构,比如我们想将所有的int类型换成string类型,对特定类型进行处理
//实现调整指定类型的描述信息
public class MySchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type == typeof(int))
{
schema.Type = "string";
schema.Description += "test";
}
}
}
注入
services.AddSwaggerGen(options =>
{
...
options.SchemaFilter<MySchemaFilter>();
});
swagger默认值
[DefaultValue(1)]
public int PageIndex{get;set;};
联系我:renhanlinbsl@163.com
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2020-04-01 c# 获取文件信息