series

  1. Develop blog projects based on ABP vNext and.NET Core – build projects using ABP CLI
  2. Develop blog projects based on ABP vNext and.NET Core – slim the project down and make it run
  3. Development blog project based on ABP vNext and.NET Core – Refinement and beautification, Swagger enter
  4. Develop blog project based on ABP vNext and.NET Core – data access and code priority
  5. Development blog project based on ABP vNext and.NET Core – add, delete, change and check custom warehouse
  6. Develop blog project based on ABP vNext and.NET Core – Uniform specification API, wrapper back model
  7. Develop blog projects based on ABP vNext and.NET Core – say Swagger, grouping, description, little green lock
  8. Develop blog projects based on ABP vNext and.NET Core – access GitHub and protect your API with JWT
  9. Develop blog project based on ABP vNext and.NET Core – exception handling and logging
  10. Develop blog projects based on ABP vNext and.NET Core – using Redis to cache data
  11. Develop blog project based on ABP vNext and.NET Core – integrate Hangfire for timed task processing
  12. Develop blog projects based on ABP vNext and.NET Core – Use AutoMapper to map objects
  13. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (Part 1)
  14. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (Part 2)
  15. Developing blog projects based on ABP vNext and.NET Core – Best Practices for Timed Tasks (PART 3)
  16. Blog Development project based on ABP vNext and.NET Core
  17. Abp vNext and.NET Core
  18. Blog Development project based on ABP vNext and.NET Core
  19. Blog Development project based on ABP vNext and.NET Core
  20. Blog Development project based on ABP vNext and.NET Core
  21. Abp vNext and.NET Core Development Blog Project – Blazor
  22. Abp vNext and.NET Core Development Blog Project – Blazor – Part 2
  23. Abp vNext and.NET Core Development Blog Project – Blazor
  24. Abp vNext and.NET Core Development Blog Project – Blazor
  25. Abp vNext and.NET Core Development Blog Project – Blazor
  26. Abp vNext and.NET Core Development Blog Project – Blazor – Part 6
  27. Abp vNext and.NET Core Development Blog Project – Blazor
  28. Abp vNext and.NET Core Development Blog Project – Blazor Series (8)
  29. Abp vNext and.NET Core Development Blog Project – Blazor Series (9)
  30. Abp vNext and.NET Core development blog project – Final release project

Before we get started, we implement a legacy issue that someone found on GitHub Issues(github.com/Meowv/Blog/…) When we group Swagger, after the IDocumentFilter interface adds the document description, the Tag that does not belong to the current group will be displayed when the group is switched.

After research and analysis, it is found that it can be solved. I don’t know if you have a better way. My implementation method is as follows:

//SwaggerDocumentFilter.cs.public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            var tags = newList<OpenApiTag>{... }#regionImplement adding custom description filter does not belong to the same group API

            var groupName = context.ApiDescriptions.FirstOrDefault().GroupName;

            var apis = context.ApiDescriptions.GetType().GetField("_source", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(context.ApiDescriptions) as IEnumerable<ApiDescription>;

            varcontrollers = 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}...Copy the code

As found in the debug code, we can get the API for which group is currently displayed from Context.apidescriptions.

Then use GetType().getField (String name, BindingFlags bindingAttr) to get _source, all the apis of the current project, which also includes some interfaces generated by ABP default.

Then filter out the API that does not belong to the current group. Use Select to query all Controller names for deduplication.

Since the Name of OpenApiTag is the same as the Name of the Controller, the tag containing the controllers’ names should be reversed to meet the requirements.


Previous article (juejin.cn/post/684490…) GitHub integration, using JWT to complete identity authentication and authorization, protect the API interface we write.

This implementation focuses on handling only exceptions that occur in the project and effectively monitoring and logging when unavoidable errors occur or when an unauthorized user invokes the interface.

Currently, calling an unauthorized interface directly returns an error page with status code 401, which is too unfriendly. We still use the uniform return model written earlier to tell the caller that you are unauthorized and cannot call my interface. As mentioned in the previous article, we will solve this problem in two ways.

Method one: Use the options.events event mechanism under the AddJwtBearer() extension method.

//MeowvBlogHttpApiHostingModule.cs.// The object provided by the application to handle events raised by the bearer, the authentication handler
	options.Events = new JwtBearerEvents
	{
	    OnChallenge = async context =>
	    {
	        // Skip the default processing logic and return the following model data
	        context.HandleResponse();
	
	        context.Response.ContentType = "application/json; charset=utf-8";
	        context.Response.StatusCode = StatusCodes.Status200OK;
	
	        var result = new ServiceResult();
	        result.IsFailed("UnAuthorized");
	
	        awaitcontext.Response.WriteAsync(result.ToJson()); }}; .Copy the code

At project startup, OnChallenge is instantiated, and if the user makes an unauthorized call, the status code of the request is assigned to 200 and model data is returned.

As shown in the figure, you can see that you have successfully returned a friendly piece of JSON data.

{
  "Code": 1."Message": "UnAuthorized"."Success": false."Timestamp": 1590226085318
}
Copy the code

Mode 2: Using the middleware.

Let’s comment out the code above in. Middleware HttpApi. Hosting add folder, create a new 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 > / / / exception handling Middleware / / / < summary > public class ExceptionHandlerMiddleware { privatereadonly RequestDelegate next;

        public ExceptionHandlerMiddleware(RequestDelegate next)
        {
            this.next = next;
        }

        /// <summary>
        /// Invoke
        /// </summary>
        /// <param name="context"></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 > / / / exception handling, return JSON / / / < summary > / / / < param name ="context"></param>
        /// <param name="message"></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()); }}}Copy the code

RequestDelegate is a type of RequestDelegate that is used to handle HTTP requests. It returns a delegate and implements an asynchronous Invoke method.

Here I have written a more general method that executes the ExceptionHandlerAsync() method directly when an exception occurs and, when no exception occurs, determines the current request state in finally, which may be 200? 404? 401? Wait, whatever it is, it’s not 200, gets the Key value of the status code enumeration to return as an error message, and finally executes ExceptionHandlerAsync() to return our custom model.

Write the middleware, and then in OnApplicationInitialization (…). Use it in.

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {...// Exception handling middlewareapp.UseMiddleware<ExceptionHandlerMiddleware>(); . }Copy the code

Can achieve the same effect, compared with he also supports the status of 401 error return, such as the us access to a nonexistent page: https://localhost:44388/aaa, also can undertake processing and friendly.

Of course, these two ways can coexist without affecting each other.

There is another way to handle exceptions, which is our Filter. Abp has implemented the global exception module by default. For details, please refer to its documentation: docs. At this point, I’m going to remove the exception handling module provided by ABP and implement one myself.

To take a look at the current exception display, let’s write an exception interface in the HelloWorldController.

//HelloWorldController.cs. [HttpGet] [Route("Exception")]
        public string Exception()
        {
            throw new NotImplementedException("This is an unimplemented exception interface."); }...Copy the code

By rights, he should be able to perform to we write ExceptionHandlerMiddleware middleware, but by our Filter to intercept, now we remove the default AbpExceptionFilter interceptor

And in the module class MeowvBlogHttpApiHostingModule ConfigureServices () method.

Configure<MvcOptions>(options =>
{
    var filterMetadata = options.Filters.FirstOrDefault(x => x is ServiceFilterAttribute attribute && attribute.ServiceType.Equals(typeof(AbpExceptionFilter)));

    / / remove AbpExceptionFilter
    options.Filters.Remove(filterMetadata);
});
Copy the code

Filters from options.Filters find AbpExceptionFilter and Remove it. Then look at the exception interface.

When we comment out our middleware, it will display something like this.

Does this page feel familiar? I’m sure anyone who has done.NET Core development has encountered it.

Ok, so far it’s working perfectly. But that’s not enough. What about implementing Filter yourself? What’s the use of implementing Filter now? We can do some logging in Filter.

In HttpApi. Hosting layer Filters, add folder to create a new MeowvBlogExceptionFilter. Cs Filter, he needs to realize our IExceptionFilter interface OnExceptionAsync () method.

//MeowvBlogExceptionFilter.cs
using Meowv.Blog.ToolKits.Helper;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Meowv.Blog.HttpApi.Hosting.Filters
{
    public class MeowvBlogExceptionFilter : IExceptionFilter
    {
        /// <summary>
        ///Exception handling
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public void OnException(ExceptionContext context)
        {
            // Log
            LoggerHelper.WriteToFile($"{context.HttpContext.Request.Path}|{context.Exception.Message}", context.Exception); }}}Copy the code

OnException(…) The method is very simple, here just do the logging operation, the rest of our middleware to handle.

Note that our own implementation of MeowvBlogExceptionFilter must be injected into the system in the module class ConfigureServices() method after removing the default AbpExceptionFilter.

. Configure<MvcOptions>(options => { ...// Add your own implementation of MeowvBlogExceptionFilter
	    options.Filters.Add(typeof(MeowvBlogExceptionFilter)); }); .Copy the code

When it comes to logging, there are many ways to deal with it, please choose the way you are familiar with, I will use log4net here for processing, just for reference.

To add the log4Net Package to the.ToolKits layer, run the install-package log4Net command to Install it, then add folder Helper, and create 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>
        ///Write the log
        /// </summary>
        /// <param name="message"></param>
        /// <param name="ex"></param>
        public static void WriteToFile(string message)
        {
            Log.Info(message);
        }

        /// <summary>
        ///Write the log
        /// </summary>
        /// <param name="message"></param>
        /// <param name="ex"></param>
        public static void WriteToFile(string message, Exception ex)
        {
            if (string.IsNullOrEmpty(message)) message = ex.Message; Log.Error(message, ex); }}}Copy the code

Add the log4net configuration file to.HttpApi.Hosting.

//log4net.config
<?xml version="1.0" encoding="utf-8" ? >
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <log4net debug="false">

    <appender name="info" type="log4net.Appender.RollingFileAppender,log4net">
      <param name="File" value="log4net/info/" />
      <param name="AppendToFile" value="true" />
      <param name="MaxSizeRollBackups" value="1"/>
      <param name="MaximumFileSize" value="5MB"/>
      <param name="RollingStyle" value="Composite" />
      <param name="DatePattern" value="yyyyMMdd\\HH" .log"" />
      <param name="StaticLogFileName" value="false" />
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern" value="%n { " system" : " Meowv.Blog" , " datetime" : " %d" , " description" : " %m" , " level" : " %p" , " info" : " %exception" }" />
      </layout>
      <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="INFO" />
        <levelMax value="INFO" />
      </filter>
    </appender>

    <appender name="error" type="log4net.Appender.RollingFileAppender,log4net">
      <param name="File" value="log4net/error/" />
      <param name="AppendToFile" value="true" />
      <param name="MaxSizeRollBackups" value="1"/>
      <param name="MaximumFileSize" value="5MB"/>
      <param name="RollingStyle" value="Composite" />
      <param name="DatePattern" value="yyyyMMdd\\HH" .log"" />
      <param name="StaticLogFileName" value="false" />
      <layout type="log4net.Layout.PatternLayout,log4net">
        <param name="ConversionPattern" value="%n { " system" : " Meowv.Blog" , " datetime" : " %d" , " description" : " %m" , " level" : " %p" , " info" : " %exception" }" />
      </layout>
      <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="ERROR" />
        <levelMax value="ERROR" />
      </filter>
    </appender>

    <root>
      <level value="ALL"></level>
      <appender-ref ref="info"/>
      <appender-ref ref="error"/>
    </root>

  </log4net>

</configuration>
Copy the code

Call… /HelloWorld/Exception, will get the log file, the content is stored in JSON format.

More information about the use of Filter can be found in the Microsoft documentation: docs.microsoft.com/zh-cn/aspne…

At this point, exception handling and logging of the system are complete, have you learned? 😁 😁 😁

Open source: github.com/Meowv/Blog/…