.NET Core基础篇之:集成Swagger文档与自定义Swagger UI

.NET Core基础篇之:集成Swagger文档与自定义Swagger UI

Swagger大家都不陌生,Swagger (OpenAPI) 是一个与编程语言无关的接口规范,用于描述项目中的 REST API。它的出现主要是节约了开发人员编写接口文档的时间,可以根据项目中的注释生成对应的可视化接口文档。

OpenAPI 规范 (openapi.json)

OpenAPI 规范是描述 API 功能的文档。该文档基于控制器和模型中的 XML属性注释。它是 OpenAPI 流的核心部分,用于驱动诸如 SwaggerUI 之类的工具。

.NET 平台下的两个主要实现Swagger的包是 Swashbuckle 和 NSwag。今天我们从 Swashbuckle 开始了解。

基础功能

1、在包管理器搜索Swashbuckle.AspNetCore并安装。
2、在Startup.cs文件内的ConfigureServices方法内添加代码。
复制public void ConfigureServices(IServiceCollection services){services.AddControllers();services.AddSwaggerGen();}
3、在Startup.cs文件内的Configure方法内添加代码。
复制app.UseSwagger();app.UseSwaggerUI(options =>{options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");options.RoutePrefix = string.Empty;});
4、修改项目的launchSettings.json文件,将launchUrl的值改为:index.html
5、准备接口
复制    [ApiController]    public class HomeController : ControllerBase    {        private readonly ILogger<HomeController> _logger;        public HomeController(ILogger<HomeController> logger)        {            _logger = logger;        }        /// <summary>        /// 获取用户信息        /// </summary>        /// <returns></returns>        [HttpGet("home/getuser")]        public string GetUser()        {            return "my name is dotnetboy";        }        /// <summary>        /// 登录成功        /// </summary>        /// <returns></returns>        [HttpPost("home/login")]        public string Login()        {            return "login success";        }        /// <summary>        /// 删除用户        /// </summary>        [HttpDelete("home/{id}")]        public string DeleteUser(string id)        {            return $"delete success,id={id}";        }    }
6、开启xml文档输出然后启动项目

扩展功能

项目描述

复制services.AddSwaggerGen(options =>{    options.SwaggerDoc("v1", new OpenApiInfo    {    Title = "测试接口文档",    Version = "v1",    Description = "测试 webapi"    });});

接口分组

在实际开发中,如果所有接口都展示在一起非常不利于相关人员查找,我们可以根据业务逻辑对相关接口进行分组,比如:登录、用户、订单、商品等等。

1、准备分组信息特性
复制/// <summary>/// 分组信息特性/// </summary>public class GroupInfoAttribute : Attribute{    /// <summary>    /// 标题    /// </summary>    public string Title { get; set; }    /// <summary>    /// 版本    /// </summary>    public string Version { get; set; }    /// <summary>    /// 描述    /// </summary>    public string Description { get; set; }}
2、准备分组枚举
复制/// <summary>/// 接口分组枚举/// </summary>public enum ApiGroupNames{    [GroupInfo(Title = "登录认证", Description = "登录相关接口", Version = "v1")]    Login,    [GroupInfo(Title = "User", Description = "用户相关接口")]    User,    [GroupInfo(Title = "User", Description = "订单相关接口")]    Order}
3、准备接口特性
复制/// <summary>/// 分组接口特性/// </summary>public class ApiGroupAttribute : Attribute, IApiDescriptionGroupNameProvider{    /// <summary>    ///     /// </summary>    /// <param name="name"></param>    public ApiGroupAttribute(ApiGroupNames name)    {        GroupName = name.ToString();    }    /// <summary>    /// 分组名称    /// </summary>    public string GroupName { get; set; }}
4、给不同接口加上特性
复制[ApiController]public class HomeController : ControllerBase{   private readonly ILogger<HomeController> _logger;   public HomeController(ILogger<HomeController> logger)    {        _logger = logger;    }    /// <summary>    /// 获取用户信息    /// </summary>    /// <returns></returns>    [HttpGet("home/getuser")]    [ApiGroup(ApiGroupNames.User)]    public string GetUser()    {        return "my name is dotnetboy";    }    /// <summary>    /// 登录成功    /// </summary>    /// <returns></returns>    [HttpPost("home/login")]    [ApiGroup(ApiGroupNames.Login)]    public string Login()    {        return "login success";    }    /// <summary>    /// 删除订单    /// </summary>    [HttpDelete("home/{id}")]    [ApiGroup(ApiGroupNames.Order)]    public string DeleteOrder(string id)    {        return $"delete success,id={id}";    }    /// <summary>    /// 留言    /// </summary>    [HttpDelete("home/message")]    public string DeleteUser(string msg)    {        return $"message:{msg}";    }}
5、修改 ConfigureServices 方法的 AddSwaggerGen
复制services.AddSwaggerGen(options =>{    options.SwaggerDoc("v1", new OpenApiInfo    {        Title = "接口文档",        Version = "v1",        Description = "测试 webapi"    });// 遍历ApiGroupNames所有枚举值生成接口文档,Skip(1)是因为Enum第一个FieldInfo是内置的一个Int值typeof(ApiGroupNames).GetFields().Skip(1).ToList().ForEach(f =>{        //获取枚举值上的特性        var info = f.GetCustomAttributes(typeof(GroupInfoAttribute), false).OfType<GroupInfoAttribute>().FirstOrDefault();        options.SwaggerDoc(f.Name, new OpenApiInfo        {            Title = info?.Title,            Version = info?.Version,            Description = info?.Description        });});    // 没有特性的接口分到NoGroup上    options.SwaggerDoc("NoGroup", new OpenApiInfo    {    Title = "无分组"    });    // 判断接口归于哪个分组    options.DocInclusionPredicate((docName, apiDescription) =>    {    if (docName == "NoGroup")    {            // 当分组为NoGroup时,只要没加特性的接口都属于这个组            return string.IsNullOrEmpty(apiDescription.GroupName);    }    else    {    return apiDescription.GroupName == docName;    }    });});
6、修改 Configure 方法的 UseSwaggerUI
复制app.UseSwaggerUI(options =>{    // 遍历ApiGroupNames所有枚举值生成接口文档    typeof(ApiGroupNames).GetFields().Skip(1).ToList().ForEach(f =>    {        //获取枚举值上的特性        var info = f.GetCustomAttributes(typeof(GroupInfoAttribute), false).OfType<GroupInfoAttribute>().FirstOrDefault();        options.SwaggerEndpoint($"/swagger/{f.Name}/swagger.json", info != null ? info.Title : f.Name);    });    options.SwaggerEndpoint("/swagger/NoGroup/swagger.json", "无分组");    options.RoutePrefix = string.Empty;});

自定义UI

前几天,前端同事和我吐槽,Swagger的原生UI太丑了,又不够直观,想找个接口还得一个个收缩展开,总之就是很难用。

  1. 不够直观
  2. 不方便查找

有了上面的两点需求何不自己实现一套UI呢?(最终还是用了第三方现成的)

文章最开始有提到OpenAPI 对应的 json 内容,大家也可以在浏览器的控制台看看,swagger ui 的数据源都来自于一个叫 swagger.json 的文件,数据源都有了,根据数据源再做一套 UI 也就不是什么难事了。

1、准备一个美观的单页面(网上找的)
2、将单页面相关内容放到项目内(记得开启静态文件读取)
复制app.UseStaticFiles();
3、将单页面指定为 UI 页面。
复制app.UseSwaggerUI(options =>{options.IndexStream = () => GetType().Assembly.GetManifestResourceStream("h.swagger.Swagger.index.html");});
4、在单页面内处理 swagger.json 数据源。
5、最终效果

Swagger UI的功能还是比较多的,比如:详情、调试。如果想自己实现一套UI要做的工作还很多。所以,拿来主义永不过时,最终我还是选择了第三方开源的项目:Knife4j

使用起来也是非常简单,先引用包:IGeekFan.AspNetCore.Knife4jUI,然后在Startup.Configure中将 app.UseSwaggerUI 替换为:

复制app.UseKnife4UI(c =>{c.RoutePrefix = string.Empty;c.SwaggerEndpoint($"/swagger/v1/swagger.json", "h.swagger.webapi v1");});

推荐阅读