ASP.NET Core使用Swagger实现接口文档并分组
每一个程序员都有重构他人代码的冲动,但是,每一个程序员都不会有写接口文档的冲动。
据我所知,在.net项目中,很多同行的中小型项目接口文档都使用Swagger,最近几个朋友一起讨论,有没有比较好用的类似Swagger接口文档开源项目,其中有朋友反馈说api太多的情况下,使用Swagger文档就是一个灾难,因为接口太多,前端开发人员很难找到自己想要的接口,因为所有的接口和接口实体类都展示在一个页面上。
说实话,我以前还真没有关注过这个问题,这两天我有个项目,接口也比较多,自己在测试接口的时候才发现确实存在这种情况。由于公司内部还有一套自己的接口文档框架,使用体验相对于Swagger更优,所以我就想Swagger怎么就不能像我们自己的框架那样,将webapi分组呢。后来查了些资料,Swagger团队早就为我想到了,结论就是Swagger也支持接口分组。
一般我们分组都是按照控制器来分组,一个webapi控制器也就是一个模块,具体实现我们往下看:
接入Swagger
1.在NuGet包管理器中安装Swashbuckle.AspNetCore
2.添加配置文件:swaggergroupconfigs.json,文件内容如下:
{ "swaggergroupconfigs": [ { "GroupName": "Web", "Title": "Web模块", "Version": "V1.0" }, { "GroupName": "Order", "Title": "订单模块", "Version": "V1.0" }, { "GroupName": "System", "Title": "系统模块", "Version": "V1.0" } ] }
3.加载分组配置文件(这里使用了环境变量):
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) => { var env = hostBuilderContext.HostingEnvironment; //optional:如果是必要的配置文件,可选就要设定为false,当文件不存在就会引发FileNotFoundException。 //reloadOnChange :如果文件被更新,是否更新IConfiguration实例的值。 configurationBuilder.AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true); configurationBuilder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); configurationBuilder.AddJsonFile($"hosting.json", optional: true, reloadOnChange: true); configurationBuilder.AddJsonFile($"hosting.{env.EnvironmentName}.json", optional: false, reloadOnChange: true).Build(); configurationBuilder.AddJsonFile($"swaggergroupconfigs.json", optional: true, reloadOnChange: true); configurationBuilder.AddJsonFile($"swaggergroupconfigs.{env.EnvironmentName}.json", optional: false, reloadOnChange: true).Build(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }).UseSerilog(dispose: true); }
4.注入Swagger服务
public void ConfigureServices(IServiceCollection services) { services.AddControllers();//Swagger接口模块分组配置 var swaggergroupconfigs = Configuration.GetSection("swaggergroupconfigs").Get<List<SwaggerGroupConfig>>(); services.AddSwaggerGen(swoption => { swoption.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo() { Version = "1.2.10", Title = "Api Document", Description = "Api Document" }); #region Swagger接口模块分组配置 swaggergroupconfigs.ForEach(group => { swoption.SwaggerDoc(group.GroupName, new OpenApiInfo { Title = group.Title, Version = group.Version }); //分组显示 }); #endregion var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径) var xmlPath = Path.Combine(basePath, "Research.Web.xml"); swoption.IncludeXmlComments(xmlPath, true); }); }
5.启用SwaggerUi中间件服务
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); //自定义中间件 app.UseRequestMiddlewares(); //启用中间件服务生成Swagger作为JSON终结点 app.UseSwagger(); //启用中间件服务对swagger-ui,指定Swagger JSON终结点 var swaggerGroupCofigs = Configuration.GetSection("swaggergroupconfigs").Get<List<SwaggerGroupConfig>>(); app.UseSwaggerUI(suoption => { suoption.SwaggerEndpoint("/swagger/v1/swagger.json", "Api Document"); suoption.RoutePrefix = string.Empty; swaggerGroupCofigs.ForEach(group => { suoption.SwaggerEndpoint($"/swagger/{group.GroupName}/swagger.json", group.Title); //分组显示 }); }); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
public class SwaggerGroupConfig { public string GroupName { get; set; } public string Title { get; set; } public string Version { get; set; } }
6.在控制器类上添加分组特性(注意:GroupName值等于分组配置文件中对应的字段值)
using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Research.Web.Common; namespace Research.Web.Controllers { [ApiExplorerSettings(GroupName = "Order")] [ApiController] [Route("Order")] public class OrderController : ControllerBase { [HttpGet("Details")] public JsonResult Details(int id) { var claims = HttpContext.User.Claims.ToList(); var list = new List<object>(); claims.ForEach(claim => { list.Add(new { key = claim.Type, value = (claim.Type.Equals("nbf") || claim.Type.Equals("exp")) ? claim.Value.UnixTimeToDateTime() : claim.Value }); }); return new JsonResult(list); } } }
6.运行,查看效果如下: