【ASP.NET Core Swagger】1、介绍
什么是Swagger/OpenAPI
Swagger是为帮助我们生成webapi文档的工具,可以直接从您的路由、控制器和模型生成漂亮的 API 文档
Swagger 是一个与语言无关的规范,用于描述 REST API。Swagger 项目已捐赠给 OpenAPI 计划,现在它被称为OpenAPI。
Nswag VS Swashbuckle?
.NET Swagger 实现类库有两个比较流行:
Swashbuckle.AspNetCore
是一个开源项目,用于生成 ASP.NET Core Web API 的 Swagger 文档。
NSwag
是另一个用于生成 Swagger 文档并将 Swagger UI 或 ReDoc 集成到 ASP.NET Core Web API 中的开源项目。此外,NSwag 还提供了为 API 生成 C# 和 TypeScript 客户端代码的方法。
Swashbuckle相关Nuget包
-
Swashbuckle.AspNetCore.Swagger
:一个 Swagger 对象模型和中间件,用于将SwaggerDocument对象公开为 JSON 端点。 -
Swashbuckle.AspNetCore.SwaggerGenSwaggerDocument
:一个 Swagger 生成器,它直接从您的路由、控制器和模型构建对象。它通常与 Swagger 端点中间件结合使用以自动公开 Swagger JSON。 -
Swashbuckle.AspNetCore.SwaggerUI
:Swagger UI 工具的嵌入式版本。它解释 Swagger JSON 以构建丰富的、可定制的体验来描述 Web API 功能。它包括用于公共方法的内置测试工具。 -
Swashbuckle.AspNetCore
:包含以上3个包,项目中可以只引用这一个包 -
Swashbuckle.AspNetCore.Newtonsoft
:如果您使用的是Newtonsoft,需要安装这个包,以确保Swagger 生成器自动支持Newtonsoft设置/属性。添加Newtonsoft支持:services.AddSwaggerGenNewtonsoftSupport();
-
Swashbuckle.AspNetCore.Annotations
:包括一组可应用于Controller、Action和Model的自定义属性,以丰富生成的 Swagger -
Swashbuckle.AspNetCore.ReDoc
: ReDoc UI 的嵌入式版本(swagger-ui 的替代方案)
github中Swashbuckle.AspNetCore源码地址:https://github.com/domaindrivendev/Swashbuckle.AspNetCore
入门案例
添加Nuget
install-package Swashbuckle.AspNetCore
添加服务、中间件
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
//生成SwaggerJSON终结点。如:/swagger/v1/swagger.json
app.UseSwagger();
app.UseSwaggerUI();
}
访问swagger ui:http://localhost:xxxx/swagger/index.html
查看生成的swagger json:http://localhost:xxxx/swagger/v1/swagger.json
Api信息和描述
builder.Services.AddSwaggerGen(options => {
//定义一个或多个Swagger 文档
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = "v1 标题",
Version = "v1",
Description = "v1描述信息",
Contact = new Microsoft.OpenApi.Models.OpenApiContact
{
Name = "fan",
Email = "410577910@qq.com",
Url = new System.Uri("https://www.cnblogs.com/fanfan-90/")
},
License = new Microsoft.OpenApi.Models.OpenApiLicense
{
Name = "许可证名字",
Url = new System.Uri("https://www.cnblogs.com/fanfan-90/")
},
Extensions = new Dictionary<string, IOpenApiExtension> {
{ "powered by", new Microsoft.OpenApi.Any.OpenApiString(".net5") }
}
});
});
xml注释
将控制器、Action、Model的xml注释显示在swagger中
项目文件添加:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
添加配置后会在bin/debug中生成一个xml文件
引用xml文件:
builder.Services.AddSwaggerGen(options => {
// using System.Reflection;
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
controller、model添加注释
/// <summary>
/// 订单接口
/// </summary>
[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
/// <summary>
/// 订单列表
/// </summary>
/// <returns></returns>
[HttpGet("orderlist")]
public IEnumerable<Order> GetList()
{
return Enumerable.Range(1, 5).Select(index => new Order
{
ID=index,
CreateTime = DateTime.Now.AddDays(index),
})
.ToArray();
}
}
/// <summary>
/// 订单
/// </summary>
public class Order
{
/// <summary>
/// 订单号
/// </summary>
public int ID { get; set; }
/// <summary>
/// 下单时间
/// </summary>
public DateTime CreateTime { get; set; }
}
如果Controller、Model在其他项目中
1、在Controller、Model所在的项目文件中添加 GenerateDocumentationFile属性
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
2、Startup
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "项目名.xml"));//如:FAN.Application.xml
使用请求上下文修改 Swagger
如果您需要根据当前请求设置一些 Swagger 元数据,您可以配置在序列化文档之前执行的过滤器。
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swagger, httpReq) =>
{
if (swagger.Tags?.Count>0 && swagger.Tags[0].Name=="Order")
{
swagger.Tags[0].Description = "订单接口描述~~~";
foreach (var path in swagger.Paths)
{
if (path.Key.ToLower()=="/order/orderlist")
{
path.Value.Operations[OperationType.Get].Summary = "订单列表~~~";
}
}
}
swagger.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}" } };
});
});
响应媒体类型
[HttpPost("createorder")]
[Produces("application/json")]//响应内容的媒体类型
public ActionResult<Order> CreateOrder([FromBody] CreateOrderInput input)
{
return new Order { };
}
自定义用户界面
修改默认Swagger UI 样式
app.UseStaticFiles();
app.UseSwaggerUI(options =>
{
options.InjectStylesheet("/swagger-ui/custom.css");
});
修改swagger json路径
UseSwagger():会生成终结点路由,通过/swagger/{documentName}/swagger.json
可以访问
以下代码可修改默认的终结点路径:
app.UseSwagger(options => { options.RouteTemplate = "/api-docs/{documentName}/swagger.json"; options.SerializeAsV2 = true; });
修改后的访问路径:/api-docs/v1/swagger.json
注意:如果您使用的是 SwaggerUI 中间件,您还需要更新其配置以反映新的端点:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/api-docs/v1/swagger.json", "My API V1");
});
文档排除指定接口
SwaggerUI中不显示指定接口,在需要排除的Controller或Action上添加特性:
[ApiExplorerSettings(IgnoreApi = true)]
支持Newtonsoft:
使用Newtonsoft对接口参数序列化
install-package Swashbuckle.AspNetCore.Newtonsoft
services.AddSwaggerGen(c =>
{
//省略....
}).AddSwaggerGenNewtonsoftSupport();//添加对Newtonsoft支持
枚举属性显示属性名
效果如下:
过时写法:
services.AddSwaggerGen(c =>{
c.DescribeAllEnumsAsStrings();
}).AddSwaggerGenNewtonsoftSupport();
新版:
services.AddSwaggerGen(options =>
{
options.SchemaFilter<EnumSchemaFilter>();
});
/// <summary>
/// 枚举显示字符串
/// </summary>
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema model, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
model.Enum.Clear();
var enumItems = context.Type.GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (var enumItem in enumItems)
{
string name = enumItem.Name;
var description = enumItem.GetCustomAttributes(typeof(DescriptionAttribute), false)?.FirstOrDefault() as DescriptionAttribute;
if (description != null)
{
name = description.Description;
}
model.Enum.Add(new OpenApiString(name));
}
}
}
}
修改swagger-ui访问路径
//SwaggerUI界面默认url是/swagger/index.html;修改后可通过 /index.html 访问SwaggerUI
app.UseSwaggerUI(c =>
{
c.RoutePrefix = string.Empty;
});
按约定忽略Action
忽略所有Get类型的Action
// ApiExplorerGetsOnlyConvention.cs
public class ApiExplorerGetsOnlyConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
action.ApiExplorer.IsVisible = action.Attributes.OfType<HttpGetAttribute>().Any();
}
}
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(c =>
c.Conventions.Add(new ApiExplorerGetsOnlyConvention())
);
...
}
SwaggerGenOptions参数类
SwaggerDoc():配置接口描述信息
OperationFilter():可通过IOperationFilter接口去添加一些公共的参数
DocumentFilter通过IDocumentFilter接口去生成控制器的标签(描述)
案例:
builder.Services.AddSwaggerGen(options =>
{
//定义一个或多个Swagger 文档
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = "v1 标题",
Version = "v1",
Description = "v1描述信息",
Contact = new Microsoft.OpenApi.Models.OpenApiContact
{
Name = "fan",
Email = "410577910@qq.com",
Url = new System.Uri("https://www.cnblogs.com/fanfan-90/")
},
License = new Microsoft.OpenApi.Models.OpenApiLicense
{
Name = "许可证名字",
Url = new System.Uri("https://www.cnblogs.com/fanfan-90/")
},
Extensions = new Dictionary<string, IOpenApiExtension> {
{ "powered by", new Microsoft.OpenApi.Any.OpenApiString(".net5") }
}
});
// using System.Reflection;
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
options.EnableAnnotations();
//options.DescribeAllEnumsAsStrings();
//忽略过时Action
options.IgnoreObsoleteActions();
//忽略过时属性
options.IgnoreObsoleteProperties();
options.MapType(typeof(SwaggerDemo.Controllers.PayType), () => new OpenApiSchema { Type = "string" });
//自定义标签(分组)
//options.TagActionsBy(api => api.HttpMethod);//按HttpMethod分组
//Action顺序
options.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}");
//Action过滤器
options.OperationFilter<AuthResponsesOperationFilter>();
//Model过滤器
options.SchemaFilter<AutoRestSchemaFilter>();
//文档过滤器
options.DocumentFilter<TagDescriptionsDocumentFilter>();
})
public class AuthResponsesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters.Add(new OpenApiParameter() { Name="param1",Description="附加参数"});
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
if (authAttributes.Any())
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
}
}
public class AutoRestSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var type = context.Type;
if (type.IsEnum)
{
schema.Extensions.Add(
"x-ms-enum",
new OpenApiObject
{
["name"] = new OpenApiString(type.Name),
["modelAsString"] = new OpenApiBoolean(true)
}
);
};
}
}
public class TagDescriptionsDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Tags = new List<OpenApiTag> {
new OpenApiTag { Name = "Products", Description = "Browse/manage the product catalog" },
new OpenApiTag { Name = "Orders", Description = "Submit orders" }
};
}
}
参考:
基础:https://www.cnblogs.com/yilezhu/p/9241261.html
WebAPI分组:https://www.cnblogs.com/toiv/archive/2018/07/28/9379249.html
过滤器:https://www.cnblogs.com/shanfeng1000/p/13476831.html
KnifeUI:https://www.cnblogs.com/igeekfan/p/IGeekFan-AspNetCore-Knife4jUI.html
https://discoverdot.net/projects/swashbuckle-aspnetcore