【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

posted @ 2022-10-16 08:37  .Neterr  阅读(604)  评论(0编辑  收藏  举报