基于abpvNext和.NETCore开发博客项目-再说Swagger,分组、描述、小绿锁
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
基于abpvNext和.NETCore开发博客项⽬-再说Swagger,分组、描述、⼩绿锁
系列⽂章
在开始本篇正⽂之前,解决⼀个 @疯疯过指出的错误,再次感谢指正。
步骤如下:
删掉.Domain.Shared层中的项⽬引⽤,添加nuget依赖包Volo.Abp.Identity.Domain.Shared,可以使⽤命令:Install-Package Volo.Abp.Identity.Domain.Shared
在.Domain层中引⽤项⽬.Domain.Shared,在模块类中添加依赖typeof(MeowvBlogDomainSharedModule)
将.EntityFrameworkCore层中的引⽤项⽬.Domain.Shared改成.Domain。
当我们的项⽬不断壮⼤,API持续增多,这时如果想要快速准确定位到某个API可能不是那么容易,需要翻半天才能找对我们的API。
于是对Swagger API⽂档分组和详细的⽂档描述就有必要了,就本项⽬⽽⾔,博客系统可以分组为:博客前台接⼝、博客后台接⼝、其它公共接⼝、JWT认证授权接⼝。
其中,博客后台组中的接⼝需要授权后才可以调⽤,需要授权那么就涉及到⾝份验证,在这⾥准备采⽤JWT(JSON WEB TOKEN)的⽅式进⾏。
分组
对Swagger进⾏分组很简单,在.Swagger层中的扩展⽅法AddSwagger(this IServiceCollection services)中多次调⽤options.SwaggerDoc(...)即可,像这样
...
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "1.0.0",
Title = "我的接⼝啊1",
Description = "接⼝描述1"
});
options.SwaggerDoc("v2", new OpenApiInfo
{
Version = "1.0.0",
Title = "我的接⼝啊2",
Description = "接⼝描述2"
});
...
...
不过这样显得有点low,然后可以转变⼀下思路使⽤遍历的⽅式进⾏。
options.SwaggerDoc(...)接收两个参数:string name, OpenApiInfo info。
name:可以理解为当前分组的前缀;OpenApiInfo:有许多可配置的参数,在这⾥我只⽤到三个,Version、Title、Description。
要注意,当在AddSwagger(...)中调⽤完后,还需要在我们的扩展⽅法UseSwaggerUI(this IApplicationBuilder app)中options.SwaggerEndpoint()使⽤它,同样的也⽤遍历的⽅法。
它接收的的参数:string url, string name。
url:这⾥的url要与前⾯配置的name参数对应。
name:我们⾃定义显⽰的分组名称。
于是可以直接在扩展⽅法中新建⼀个内部类:SwaggerApiInfo
internal class SwaggerApiInfo
{
/// <summary>
/// URL前缀
/// </summary>
public string UrlPrefix { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// <see cref="Microsoft.OpenApi.Models.OpenApiInfo"/>
/// </summary>
public OpenApiInfo OpenApiInfo { get; set; }
}
然后新建⼀个List<SwaggerApiInfo>⼿动为其初始化⼀些值。
...
/// <summary>
/// Swagger分组信息,将进⾏遍历使⽤
/// </summary>
private static readonly List<SwaggerApiInfo> ApiInfos = new List<SwaggerApiInfo>()
{
new SwaggerApiInfo
{
UrlPrefix = Grouping.GroupName_v1,
Name = "博客前台接⼝",
OpenApiInfo = new OpenApiInfo
{
Version = version,
Title = "阿星Plus - 博客前台接⼝",
Description = description
}
},
new SwaggerApiInfo
{
UrlPrefix = Grouping.GroupName_v2,
Name = "博客后台接⼝",
OpenApiInfo = new OpenApiInfo
{
Version = version,
Title = "阿星Plus - 博客后台接⼝",
Description = description
}
},
new SwaggerApiInfo
{
UrlPrefix = Grouping.GroupName_v3,
Name = "通⽤公共接⼝",
OpenApiInfo = new OpenApiInfo
{
Version = version,
Title = "阿星Plus - 通⽤公共接⼝",
Description = description
}
},
new SwaggerApiInfo
{
UrlPrefix = Grouping.GroupName_v4,
Name = "JWT授权接⼝",
OpenApiInfo = new OpenApiInfo
{
Version = version,
Title = "阿星Plus - JWT授权接⼝",
Description = description
}
}
};
...
version:我们将其配置在appsettings.json中,做到动态可以修改。
//AppSettings.cs
...
/// <summary>
/// ApiVersion
/// </summary>
public static string ApiVersion => _config["ApiVersion"];
...
//appsettings.json
{
...
"ApiVersion": "1.0.0"
...
}
description:因为多次使⽤,就定义⼀个变量,内容⾃拟主要是⼀些介绍性的描述,将在Swagger界⾯进⾏显⽰。
UrlPrefix:分别为,v1,v2,v3,v4。
在Domain.Shared层中为其定义好常量
//MeowvBlogConsts.cs
...
/// <summary>
/// 分组
/// </summary>
public static class Grouping
{
/// <summary>
/// 博客前台接⼝组
/// </summary>
public const string GroupName_v1 = "v1";
/// <summary>
/// 博客后台接⼝组
/// </summary>
public const string GroupName_v2 = "v2";
/// <summary>
/// 其他通⽤接⼝组
/// </summary>
public const string GroupName_v3 = "v3";
/// <summary>
/// JWT授权接⼝组
/// </summary>
public const string GroupName_v4 = "v4";
}
...
现在修改扩展⽅法AddSwagger(...),遍历List<SwaggerApiInfo>。
...
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
return services.AddSwaggerGen(options =>
{
//options.SwaggerDoc("v1", new OpenApiInfo
//{
// Version = "1.0.0",
// Title = "我的接⼝啊",
// Description = "接⼝描述"
//});
// 遍历并应⽤Swagger分组信息
ApiInfos.ForEach(x =>
{
options.SwaggerDoc(x.UrlPrefix, x.OpenApiInfo);
});
...
});
}
...
在扩展⽅法UseSwaggerUI(...)使⽤,通⽤也需要遍历。
...
// 遍历分组信息,⽣成Json
ApiInfos.ForEach(x =>
{
options.SwaggerEndpoint($"/swagger/{x.UrlPrefix}/swagger.json", );
});
...
细⼼的同学可以发现,我们前⼏篇⽂章打开Swagger⽂档的时候都是需要⼿动更改URL地址:.../swagger才能正确进⼊,其实Swagger是⽀持配置路由的。
同时咱们也将页⾯Title也给改了吧。
看下⾯UseSwaggerUI(...)完整代码:
...
/// <summary>
/// UseSwaggerUI
/// </summary>
/// <param name="app"></param>
public static void UseSwaggerUI(this IApplicationBuilder app)
{
eSwaggerUI(options =>
{
// 遍历分组信息,⽣成Json
ApiInfos.ForEach(x =>
{
options.SwaggerEndpoint($"/swagger/{x.UrlPrefix}/swagger.json", );
});
// 模型的默认扩展深度,设置为 -1 完全隐藏模型
options.DefaultModelsExpandDepth(-1);
// API⽂档仅展开标记
options.DocExpansion(DocExpansion.List);
// API前缀设置为空
options.RoutePrefix = string.Empty;
// API页⾯Title
options.DocumentTitle = " 接⼝⽂档 - 阿星Plus ";
});
}
...
options.DefaultModelsExpandDepth(-1);是模型的默认扩展深度,设置为 -1 完全隐藏模型。
options.DocExpansion(DocExpansion.List);代表API⽂档仅展开标记,不默然展开所有接⼝,需要我们⼿动去点击才展开,可以⾃⾏查看DocExpansion。
options.RoutePrefix = string.Empty;代表路由设置为空,直接打开页⾯就可以访问了。
options.DocumentTitle = " 接⼝⽂档 - 阿星Plus ";是设置⽂档页⾯的标题的。
完成以上操作,在Controller中使⽤ Attribute:[ApiExplorerSettings(GroupName = ...)]指定是哪个分组然后就可以愉快的使⽤了。
默认不指定的话就是全部都有,⽬前只有两个Controller,我们将HelloWorldController设置成v3,BlogController设置成v1。
//HelloWorldController.cs
...
[ApiExplorerSettings(GroupName = Grouping.GroupName_v3)]
public class HelloWorldController : AbpController
{
...
}
...
//BlogController.cs
...
[ApiExplorerSettings(GroupName = Grouping.GroupName_v1)]
public class BlogController : AbpController
{
}
...
编译运⾏,打开我们的Swagger⽂档看⼀下。
⾃⼰试着换切换⼀下分组试试吧,⼤功告成。
描述
在Swagger⽂档中,默认只显⽰我们的Controller的名称,其实他也是⽀持描述信息的,这是就需要我们⾃⾏扩展了。
在.Swagger层新建⼀个⽂件夹Filters,添
加SwaggerDocumentFilter类来实现IDocumentFilter接⼝。
//SwaggerDocumentFilter.cs
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
namespace Meowv.Blog.Swagger.Filters
{
/// <summary>
/// 对应Controller的API⽂档描述信息
/// </summary>
public class SwaggerDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var tags = new List<OpenApiTag>
{
new OpenApiTag {
Name = "Blog",
Description = "个⼈博客相关接⼝",
ExternalDocs = new OpenApiExternalDocs { Description = "包含:⽂章/标签/分类/友链" }
}
new OpenApiTag {
Name = "HelloWorld",
Description = "通⽤公共接⼝",
ExternalDocs = new OpenApiExternalDocs { Description = "这⾥是⼀些通⽤的公共接⼝" }
}
};
// 按照Name升序排序
swaggerDoc.Tags = tags.OrderBy(x => ).ToList();
}
}
}
实现Apply(...)⽅法后,使⽤Linq语法对⽂档排个序,然后最重要的使⽤这个Filter,在扩展⽅法AddSwagger(...)中使⽤
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
return services.AddSwaggerGen(options =>
{
...
// 应⽤Controller的API⽂档描述信息
options.DocumentFilter<SwaggerDocumentFilter>();
});
}
再打开Swagger⽂档看看效果。
ok,此时描述信息也出来了。
⼩绿锁
在Swagger⽂档中开启⼩绿锁是⾮常简单的,只需添加⼀个包:Swashbuckle.AspNetCore.Filters,直接使⽤命令安装:Install-Package Swashbuckle.AspNetCore.Filters 然后再扩展⽅法AddSwagger(this IServiceCollection services)中调⽤
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
return services.AddSwaggerGen(options =>
{
...
var security = new OpenApiSecurityScheme
{
Description = "JWT模式授权,请输⼊ Bearer {Token} 进⾏⾝份验证",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
options.AddSecurityDefinition("oauth2", security);
options.AddSecurityRequirement(new OpenApiSecurityRequirement { { security, new List<string>() } });
options.OperationFilter<AddResponseHeadersFilter>();
options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
options.OperationFilter<SecurityRequirementsOperationFilter>();
...
});
}
以上便实现了在Swagger⽂档中显⽰⼩绿锁,我们new的OpenApiSecurityScheme对象,具体参数⼤家可以⾃⾏看⼀下注释就明⽩具体含义。
分别调
⽤options.AddSecurityDefinition(...)、options.AddSecurityRequiremen(...)、options.OperationFilter(...),编译运⾏,打开瞅瞅。
现在只是做了⼩绿锁的显⽰,但是并没有实际意义,因为在.net core中还需要配置我们的⾝份认证授权代码,才能具体发挥其真正的作⽤,所以⽬前我们的api还是处于裸奔状态,谁都能调⽤你的api,等你发现你写的⽂章都被别⼈删了,你都不知道为什么。
实现JWT,将在下篇⽂章中详细说明,本篇到这⾥就结束了,我们完善了Swagger⽂档,给接⼝加了分组、描述,还有⼩绿锁。
⽼铁,你学会了吗?。