在开始之前,我们实现一个之前的遗留问题,这个问题是有人在GitHub Issues(https://github.com/Meowv/Blog/issues/8)上提出来的,就是当我们对Swagger进行分组,实现IDocumentFilter接口添加了文档描述信息后,切换分组时会显示不属于当前分组的Tag。
经过研究和分析发现,是可以解决的,我不知道大家有没有更好的办法,我的实现方法请看:
//SwaggerDocumentFilter.cs ... public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { var tags = new List<OpenApiTag>{...} #region 实现添加自定义描述时过滤不属于同一个分组的API var groupName = context.ApiDescriptions.FirstOrDefault().GroupName; var apis = context.ApiDescriptions.GetType().GetField("_source", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(context.ApiDescriptions) as IEnumerable<ApiDescription>; var controllers = apis.Where(x => x.GroupName != groupName).Select(x => ((ControllerActionDescriptor)x.ActionDescriptor).ControllerName).Distinct(); swaggerDoc.Tags = tags.Where(x => !controllers.Contains(x.Name)).OrderBy(x => x.Name).ToList(); #endregion } ...根据调试代码发现,我们可以从context.ApiDescriptions获取到当前显示的是哪一个分组下的API。
然后使用GetType().GetField(string name, BindingFlags bindingAttr)获取到_source,当前项目的所有API,里面同时也包含了ABP默认生成的一些接口。
再将API中不属于当前分组的API筛选掉,用Select查询出所有的Controller名称进行去重。
因为OpenApiTag中的Name名称与Controller的Name是一致的,所以最后将包含controllers名称的tag查询出来取反,即可满足需求。
上一篇文章(https://www.cnblogs.com/meowv/p/12935693.html)集成了GitHub,使用JWT的方式完成了身份认证和授权,保护了我们写的API接口。
本篇主要实现对项目中出现的异常仅需处理,当出现不可避免的错误时,或者未授权用户调用接口时,可以进行有效的监控和日志记录。
目前调用未授权接口,会直接返回一个状态码为401的错误页面,这样显得太不友好,我们还是用之前写的统一返回模型来告诉调用者,你是未授权的,调不了我的接口,上篇也有提到过,我们将用两种方式来解决。
方式一 :使用AddJwtBearer()扩展方法下面的options.Events事件机制。
//MeowvBlogHttpApiHostingModule.cs ... //应用程序提供的对象,用于处理承载引发的事件,身份验证处理程序 options.Events = new JwtBearerEvents { OnChallenge = async context => { // 跳过默认的处理逻辑,返回下面的模型数据 context.HandleResponse(); context.Response.ContentType = "application/json;charset=utf-8"; context.Response.StatusCode = StatusCodes.Status200OK; var result = new ServiceResult(); result.IsFailed("UnAuthorized"); await context.Response.WriteAsync(result.ToJson()); } }; ...在项目启动时,实例化了OnChallenge,如果用户调用未授权,将请求的状态码赋值为200,并返回模型数据。
如图所示,可以看到已经成功返回了一段比较友好的JSON数据。
{ "Code": 1, "Message": "UnAuthorized", "Success": false, "Timestamp": 1590226085318 }方式二 :使用中间件的方式。
我们注释掉上面的代码,在.HttpApi.Hosting添加文件夹Middleware,新建一个中间件ExceptionHandlerMiddleware.cs
using Meowv.Blog.ToolKits.Base; using Meowv.Blog.ToolKits.Extensions; using Microsoft.AspNetCore.Http; using System; using System.Net; using System.Threading.Tasks; namespace Meowv.Blog.HttpApi.Hosting.Middleware { /// <summary> /// 异常处理中间件 /// </summary> public class ExceptionHandlerMiddleware { private readonly RequestDelegate next; public ExceptionHandlerMiddleware(RequestDelegate next) { this.next = next; } /// <summary> /// Invoke /// </summary> /// <param></param> /// <returns></returns> public async Task Invoke(HttpContext context) { try { await next(context); } catch (Exception ex) { await ExceptionHandlerAsync(context, ex.Message); } finally { var statusCode = context.Response.StatusCode; if (statusCode != StatusCodes.Status200OK) { Enum.TryParse(typeof(HttpStatusCode), statusCode.ToString(), out object message); await ExceptionHandlerAsync(context, message.ToString()); } } } /// <summary> /// 异常处理,返回JSON /// </summary> /// <param></param> /// <param></param> /// <returns></returns> private async Task ExceptionHandlerAsync(HttpContext context, string message) { context.Response.ContentType = "application/json;charset=utf-8"; var result = new ServiceResult(); result.IsFailed(message); await context.Response.WriteAsync(result.ToJson()); } } }RequestDelegate是一种请求委托类型,用来处理HTTP请求的函数,返回的是delegate,实现异步的Invoke方法。