Net6 扩展你的Swagger,让它变的更强大,更持久(哈哈哈)
十年河东,十年河西,莫欺少年穷
学无止境,精益求精
net6集成了swagger的基础功能,但功能不够用
因此只能自定义扩展方法了,如下
1、集成Jwt授权
将
builder.Services.AddSwaggerGen();
改成
builder.Services.AddSwaggerGen(c => { var scheme = new OpenApiSecurityScheme() { Description = "Authorization header. \r\nExample: 'Bearer abcdefxxx'", Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Authorization" }, Scheme = "oauth2", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }; c.AddSecurityDefinition("Authorization", scheme); var requirement = new OpenApiSecurityRequirement(); requirement[scheme] = new List<string>(); c.AddSecurityRequirement(requirement); });
2、增加Swagger注释
增加swagger注释后,测试人员/前段开发人员就可以自己看文档了,省的在开发人员面前叽叽歪歪
2.1、设置Model层和Api层项目XML输出
代码如下:
builder.Services.AddSwaggerGen(c => { var basePath = Path.GetDirectoryName(AppContext.BaseDirectory); //c.IncludeXmlComments(Path.Combine(basePath, Assembly.GetExecutingAssembly().GetName().Name+".xml"), true); c.IncludeXmlComments(Path.Combine(basePath, "swap.xml"), true); c.IncludeXmlComments(Path.Combine(basePath, "swapModels.xml"), true); // var scheme = new OpenApiSecurityScheme() { Description = "Authorization header. \r\nExample: 'Bearer 12345abcdef'", Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Authorization" }, Scheme = "oauth2", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }; c.AddSecurityDefinition("Authorization", scheme); var requirement = new OpenApiSecurityRequirement(); requirement[scheme] = new List<string>(); c.AddSecurityRequirement(requirement); });
3、自定义属性隐藏及Api方法隐藏
3.1、Api层新建swagger过滤器
using Microsoft.OpenApi.Models; using swapCommon;//HiddenFieldAttribute 和 HiddenAttribute 在公共类库存 using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Reflection; using System.Text; namespace swapInit { public class HiddenApiFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { foreach (var item in context.ApiDescriptions) { if (item.TryGetMethodInfo(out MethodInfo methodInfo)) { if (methodInfo.ReflectedType.CustomAttributes.Any(t => t.AttributeType == typeof(HiddenAttribute)) || methodInfo.CustomAttributes.Any(t => t.AttributeType == typeof(HiddenAttribute))) { var key = "/" + item.RelativePath.TrimEnd('/'); if (key.Contains("?")) { int idx = key.IndexOf("?", StringComparison.Ordinal); key = key.Substring(0, idx); } if (swaggerDoc.Paths.ContainsKey(key)) { swaggerDoc.Paths.Remove(key); } } } } } } public class HiddenFieldFilter : ISchemaFilter { public void Apply(OpenApiSchema schema, SchemaFilterContext context) { if (schema?.Properties == null) { return; } var name = context.Type.FullName; var excludedProperties = context.Type.GetProperties(); foreach (var property in excludedProperties) { var attribute = property.GetCustomAttribute<HiddenFieldAttribute>(); if (attribute != null && schema.Properties.ContainsKey(ToLowerStart(property.Name))) { schema.Properties.Remove(ToLowerStart(property.Name)); } }; } public static string ToLowerStart( string source) { if (string.IsNullOrWhiteSpace(source)) { return source; } var start = source.Substring(0, 1); return $"{start.ToLower()}{source.Substring(1, source.Length - 1)}"; } } }
3.2、公共类库层新建属性类
using System; using System.Collections.Generic; using System.Text; namespace swapCommon { [AttributeUsage(AttributeTargets.Property)] public class HiddenFieldAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class HiddenAttribute : Attribute { } }
3.3、引用上述过滤器
builder.Services.AddSwaggerGen(c => { var basePath = Path.GetDirectoryName(AppContext.BaseDirectory); //c.IncludeXmlComments(Path.Combine(basePath, Assembly.GetExecutingAssembly().GetName().Name+".xml"), true); c.IncludeXmlComments(Path.Combine(basePath, "swap.xml"), true); c.IncludeXmlComments(Path.Combine(basePath, "swapModels.xml"), true); // c.DocumentFilter<HiddenApiFilter>(); c.SchemaFilter<HiddenFieldFilter>(); // var scheme = new OpenApiSecurityScheme() { Description = "Authorization header. \r\nExample: 'Bearer 12345abcdef'", Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Authorization" }, Scheme = "oauth2", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }; c.AddSecurityDefinition("Authorization", scheme); var requirement = new OpenApiSecurityRequirement(); requirement[scheme] = new List<string>(); c.AddSecurityRequirement(requirement); });
3.4、测试Swagger过滤器
3.4.1、定义一个Post方法
/// <summary> /// 演示字段隐藏 /// </summary> /// <param name="stdto"></param> /// <returns></returns> [HttpPost] [Route("GetStdto")] public IActionResult GetStdto([FromBody]stdto stdto) { var dt = DateTime.Now; return Ok(dt ); }
3.4.2、定义参数实体
有了身份证号,可以自动计算出年龄等场景,总之不想让测试和前端人员看到,省的他们叽叽歪歪
public class stdto { public string name { get; set; } public string IdCard { get; set; } /// <summary> /// 该字段会在swagger中隐藏 /// </summary> [HiddenField] public int age { get; set; } }
3.5、[Hidden]隐藏 Action 方法
不想让测试或前端在swagger上看到某方法,那就隐藏,省的他们叽叽歪歪
/// <summary> /// 演示Api隐藏 ,该方法不会展示在swagger上 /// </summary> /// <param name="Hs">https://www.cnblogs.com/catcher1994/p/responsecaching.html</param> /// <returns></returns> [HttpGet] [Hidden] [Route("GetTime")] public IActionResult GetTime() { var dt = DateTime.Now; return Ok(dt); }
不作截图演示了。
4、规定项目时间返回格式
严格来说这个和swagger没关,但...分享是一种美德
4.1、公共类库层先建JsonResult格式化类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; namespace swapCommon.Ext { public class JsonOptionsExt : JsonConverter<DateTime> { private readonly string Format; public JsonOptionsExt(string format = "yyyy-MM-dd HH:mm:ss") { Format = format; } public override void Write(Utf8JsonWriter writer, DateTime date, JsonSerializerOptions options) { writer.WriteStringValue(date.ToString(Format)); } public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 获取时间类型的字符串 var dt = reader.GetString(); if (!string.IsNullOrEmpty(dt)) { //将日期与时间之间的"T"替换为一个空格,将结尾的"Z"去掉,否则会报错 dt = dt.Replace("T", " ").Replace("Z", ""); //取到秒,毫秒内容也要去掉,经过测试,不去掉会报错 if (dt.Length > 19) { dt = dt.Substring(0, 19); } return DateTime.ParseExact(dt, Format, null); } return DateTime.Now; } } }
4.2、添加控制器时引用
builder.Services.AddControllers()
改成
builder.Services.AddControllers().AddJsonOptions(options => { //时间格式化响应 options.JsonSerializerOptions.Converters.Add(new JsonOptionsExt()); });
改好后,时间的返回就统一了
ok,截止到这儿,就over了,希望前端和测试不要骂我。
2022年12月5日补充如下:
关于时间格式为 "yyyy-MM-dd HH:mm:ss" 也可以不使用自定义的JsonOptionsExt
添加Nutget引用,Microsoft.AspNetCore.Mvc.NewtonsoftJson V6.0.6
AddControllers()代码段替换为:
builder.Services.AddControllers().AddNewtonsoftJson(options => { options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; options.SerializerSettings.ContractResolver = new DefaultContractResolver(); });
其中
options.SerializerSettings.ContractResolver = new DefaultContractResolver(); /*支持接收JOBject动态参数*/
JOBject 动态参数,例如
[AllowAnonymous] [HttpPost] [Route("TestAlter")] public IActionResult TestAlter([FromBody] JObject data) { return Ok(data); }
可接受任意json
@天才卧龙