长沙竞价网站建设价格,网站架构设计师薪资水平,青岛网站设计公司哪家好,东莞市大朗镇在开始之前#xff0c;我们实现一个之前的遗留问题#xff0c;这个问题是有人在GitHub Issues(https://github.com/Meowv/Blog/issues/8)上提出来的#xff0c;就是当我们对Swagger进行分组#xff0c;实现IDocumentFilter接口添加了文档描述信息后#xff0c;切换分组时会… 在开始之前我们实现一个之前的遗留问题这个问题是有人在GitHub Issues(https://github.com/Meowv/Blog/issues/8)上提出来的就是当我们对Swagger进行分组实现IDocumentFilter接口添加了文档描述信息后切换分组时会显示不属于当前分组的Tag。经过研究和分析发现是可以解决的我不知道大家有没有更好的办法我的实现方法请看//SwaggerDocumentFilter.cs
...public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context){var tags new ListOpenApiTag{...}#region 实现添加自定义描述时过滤不属于同一个分组的APIvar groupName context.ApiDescriptions.FirstOrDefault().GroupName;var apis context.ApiDescriptions.GetType().GetField(_source, BindingFlags.NonPublic | BindingFlags.Instance).GetValue(context.ApiDescriptions) as IEnumerableApiDescription;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查询出来取反即可满足需求。上一篇文章集成了GitHub使用JWT的方式完成了身份认证和授权保护了我们写的API接口。本篇主要实现对项目中出现的异常仅需处理当出现不可避免的错误时或者未授权用户调用接口时可以进行有效的监控和日志记录。目前调用未授权接口会直接返回一个状态码为401的错误页面这样显得太不友好我们还是用之前写的统一返回模型来告诉调用者你是未授权的调不了我的接口上篇也有提到过我们将用两种方式来解决。方式一 使用AddJwtBearer()扩展方法下面的options.Events事件机制。//MeowvBlogHttpApiHostingModule.cs
...//应用程序提供的对象用于处理承载引发的事件身份验证处理程序options.Events new JwtBearerEvents{OnChallenge async context {// 跳过默认的处理逻辑返回下面的模型数据context.HandleResponse();context.Response.ContentType application/json;charsetutf-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.csusing 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/// 异常处理中间件/// /summarypublic class ExceptionHandlerMiddleware{private readonly RequestDelegate next;public ExceptionHandlerMiddleware(RequestDelegate next){this.next next;}/// summary/// Invoke/// /summary/// param namecontext/param/// returns/returnspublic 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 namecontext/param/// param namemessage/param/// returns/returnsprivate async Task ExceptionHandlerAsync(HttpContext context, string message){context.Response.ContentType application/json;charsetutf-8;var result new ServiceResult();result.IsFailed(message);await context.Response.WriteAsync(result.ToJson());}}
}
RequestDelegate是一种请求委托类型用来处理HTTP请求的函数返回的是delegate实现异步的Invoke方法。这里我写了一个比较通用的方法当出现异常时直接执行ExceptionHandlerAsync()方法当没有异常发生时在finally中判断当前请求状态可能是200404401等等不管它是什么反正不是200获取到状态码枚举的Key值用来当作错误信息返回最后也执行ExceptionHandlerAsync()方法返回我们自定义的模型。写好了中间件然后在OnApplicationInitialization(...)中使用它。 public override void OnApplicationInitialization(ApplicationInitializationContext context){...// 异常处理中间件app.UseMiddlewareExceptionHandlerMiddleware();...}
同样可以达到效果相比之下他还支持状态非401的错误返回比如我们访问一个不存在的页面https://localhost:44388/aaa 也可以友好的进行处理。当然这两种方式可以共存互不影响。还有一种处理异常的方式就是我们的过滤器Filterabp已经默认为我们实现了全局的异常模块详情可以看其文档https://docs.abp.io/zh-Hans/abp/latest/Exception-Handling 在这里我准备移除abp提供的异常处理模块自己实现一个。先看一下目前的异常显示情况我们在HelloWorldController中写一个异常接口。//HelloWorldController.cs
...[HttpGet][Route(Exception)]public string Exception(){throw new NotImplementedException(这是一个未实现的异常接口);}
...
按理说他应该会执行到我们写的ExceptionHandlerMiddleware中间件中去但是被我们的Filter进行拦截了现在我们移除默认的拦截器AbpExceptionFilter还是在模块类MeowvBlogHttpApiHostingModuleConfigureServices()方法中。ConfigureMvcOptions(options
{var filterMetadata options.Filters.FirstOrDefault(x x is ServiceFilterAttribute attribute attribute.ServiceType.Equals(typeof(AbpExceptionFilter)));// 移除 AbpExceptionFilteroptions.Filters.Remove(filterMetadata);
});
从options.Filters中找到AbpExceptionFilter然后Remove掉此时再看一下有异常的接口。当我们注释掉我们的中间件时他就会显示如下图这样。这个页面有没有很熟悉的感觉相信做过.net core开发的都遇到过吧。ok现在为止已经完美显示了。但到这里还远远不够说好的自己实现Filter呢我们现在实现Filter又有什么用呢我们可以在Filter中可以做一些日志记录。在.HttpApi.Hosting层添加文件夹Filters新建一个MeowvBlogExceptionFilter.cs的Filter他需要实现我们的IExceptionFilter接口的OnExceptionAsync()方法即可。//MeowvBlogExceptionFilter.cs
using Meowv.Blog.ToolKits.Helper;
using Microsoft.AspNetCore.Mvc.Filters;namespace Meowv.Blog.HttpApi.Hosting.Filters
{public class MeowvBlogExceptionFilter : IExceptionFilter{/// summary/// 异常处理/// /summary/// param namecontext/param/// returns/returnspublic void OnException(ExceptionContext context){// 日志记录LoggerHelper.WriteToFile(${context.HttpContext.Request.Path}|{context.Exception.Message}, context.Exception);}}
}
OnException(...)方法很简单这里只做了记录日志的操作剩下的交给我们中间件去处理吧。注意一定要在移除默认AbpExceptionFilter后将我们自己实现的MeowvBlogExceptionFilter在模块类ConfigureServices()方法中注入到系统。...ConfigureMvcOptions(options {...// 添加自己实现的 MeowvBlogExceptionFilteroptions.Filters.Add(typeof(MeowvBlogExceptionFilter));});
...
说到日志就有很多种处理方式请选择你熟悉的方式我这里将使用log4net进行处理仅供参考。在.ToolKits层添加log4net包使用命令安装Install-Package log4net然后添加文件夹Helper新建一个LoggerHelper.cs。//LoggerHelper.cs
using log4net;
using log4net.Config;
using log4net.Repository;
using System;
using System.IO;namespace Meowv.Blog.ToolKits.Helper
{public static class LoggerHelper{private static readonly ILoggerRepository Repository LogManager.CreateRepository(NETCoreRepository);private static readonly ILog Log LogManager.GetLogger(Repository.Name, NETCorelog4net);static LoggerHelper(){XmlConfigurator.Configure(Repository, new FileInfo(log4net.config));}/// summary/// 写日志/// /summary/// param namemessage/param/// param nameex/parampublic static void WriteToFile(string message){Log.Info(message);}/// summary/// 写日志/// /summary/// param namemessage/param/// param nameex/parampublic static void WriteToFile(string message, Exception ex){if (string.IsNullOrEmpty(message))message ex.Message;Log.Error(message, ex);}}
}
在.HttpApi.Hosting中添加log4net配置文件log4net.config配置文件如下//log4net.config
?xml version1.0 encodingutf-8 ?
configurationconfigSectionsp namelog4net typelog4net.Config.Log4NetConfigurationSectionHandler, log4net//configSectionslog4net debugfalseappender nameinfo typelog4net.Appender.RollingFileAppender,log4netparam nameFile valuelog4net/info/ /param nameAppendToFile valuetrue /param nameMaxSizeRollBackups value-1/param nameMaximumFileSize value5MB/param nameRollingStyle valueComposite /param nameDatePattern valueyyyyMMdd\\HHquot;.logquot; /param nameStaticLogFileName valuefalse /layout typelog4net.Layout.PatternLayout,log4netparam nameConversionPattern value%n
{quot;systemquot;: quot;Meowv.Blogquot;,quot;datetimequot;: quot;%dquot;,quot;descriptionquot;: quot;%mquot;,quot;levelquot;: quot;%pquot;,quot;infoquot;: quot;%exceptionquot;
} //layoutfilter typelog4net.Filter.LevelRangeFilterlevelMin valueINFO /levelMax valueINFO //filter/appenderappender nameerror typelog4net.Appender.RollingFileAppender,log4netparam nameFile valuelog4net/error/ /param nameAppendToFile valuetrue /param nameMaxSizeRollBackups value-1/param nameMaximumFileSize value5MB/param nameRollingStyle valueComposite /param nameDatePattern valueyyyyMMdd\\HHquot;.logquot; /param nameStaticLogFileName valuefalse /layout typelog4net.Layout.PatternLayout,log4netparam nameConversionPattern value%n
{quot;systemquot;: quot;Meowv.Blogquot;,quot;datetimequot;: quot;%dquot;,quot;descriptionquot;: quot;%mquot;,quot;levelquot;: quot;%pquot;,quot;infoquot;: quot;%exceptionquot;
} //layoutfilter typelog4net.Filter.LevelRangeFilterlevelMin valueERROR /levelMax valueERROR //filter/appenderrootlevel valueALL/levelappender-ref refinfo/appender-ref referror//root/log4net/configuration
此时再去调用 .../HelloWorld/Exception将会得到日志文件内容是以JSON格式进行存储的。关于Filter的更多用法可以参考微软官方文档https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/filters到这里系统的异常处理和日志记录便完成了你学会了吗????????????开源地址https://github.com/Meowv/Blog/tree/blog_tutorial