【愚公系列】2023年02月 WMS智能仓储系统-010.全局过滤、中间件、格式化配置

举报
愚公搬代码 发表于 2023/02/28 22:50:13 2023/02/28
【摘要】 前言本文主要讲解程序得全局配置,主要包含内容有全局过滤中间件格式化配置 一、全局过滤 1.全局行为过滤的概念.NET Core提供了5大过滤器供我们用来处理请求前后需要执行的代码。Authentication Filter(授权过滤器):授权过滤器最先运行,用于确定是否已针对请求为用户授权。 如果请求未获授权,授权过滤器可以让管道短路。public class AuthonizationF...

前言

本文主要讲解程序得全局配置,主要包含内容有

  • 全局过滤
  • 中间件
  • 格式化配置

一、全局过滤

1.全局行为过滤的概念

.NET Core提供了5大过滤器供我们用来处理请求前后需要执行的代码。

  • Authentication Filter(授权过滤器):授权过滤器最先运行,用于确定是否已针对请求为用户授权。 如果请求未获授权,授权过滤器可以让管道短路。
public class AuthonizationFilter :Attribute,IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            //这里可以做复杂的权限控制操作
            if (context.HttpContext.User.Identity.Name != "1") //简单的做一个示范
            {
                //未通过验证则跳转到无权限提示页
                RedirectToActionResult content = new RedirectToActionResult("NoAuth", "Exception", null);
                context.Result = content;
            }
        }
    }
  • Resource Filter(资源过滤器):可以通过Resource Filter 进行资源缓存、防盗链等操作。 使用Resource Filter 要求实现IResourceFilter 抽象接口。
public class ResourceFilter : Attribute,IResourceFilter
{
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        // 执行完后的操作
    }

    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        // 执行中的过滤器管道
    }
}
  • ActionFilter(操作过滤器):可以通过ActionFilter 拦截 每个执行的方法进行一系列的操作,比如:执行操作日志、参数验证,权限控制 等一系列操作。
public class ActionFilter : Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //执行完成....
        }
 
        public void OnActionExecuting(ActionExecutingContext context)
        {
            //执行中...
        }
    }
  • ExceptionFilter(异常过滤器):可以进行全局的异常日志收集 等操作。
public class ExecptionFilter : Attribute, IExceptionFilter
  {
        private ILogger<ExecptionFilter> _logger;
        //构造注入日志组件
        public ExecptionFilter(ILogger<ExecptionFilter> logger)
        {
            _logger = logger;
        }
 
        public void OnException(ExceptionContext context)
        {
            //日志收集
            _logger.LogError(context.Exception, context?.Exception?.Message??"异常");
        }
    }
  • ResultFilter(结果过滤器):结果过滤器,可以对结果进行格式化、大小写转换等一系列操作。
public class ResultFilter : Attribute, IResultFilter
 {
        public void OnResultExecuted(ResultExecutedContext context)
        { 
            // 在结果执行之后调用的操作...
        }
 
        public void OnResultExecuting(ResultExecutingContext context)
        {
            // 在结果执行之前调用的一系列操作
        }
    }

2.全局行为过滤的注册方式

2.1 方法注册

[AuthonizationFilter()]
public IActionResult Index()
{

}

2.2 控制器注册

[AuthonizationFilter()]
public class FirstController : Controller
{
      private ILogger<FirstController> _logger;

      public FirstController(ILogger<FirstController> logger)
      {
          _logger = logger;
      }

      public IActionResult Index()
      {
          return View();
      }
}

2.3 全局注册

public void ConfigureServices(IServiceCollection services)
{
      //全局注册异常过滤器
      services.AddControllersWithViews(option=> {
          option.Filters.Add<ExecptionFilter>();
      });
}

2.4 TypeFilter 和 ServiceFilter 注册方式

ServiceFilter 我们必须在 Startup.cs 中注册.TypeFilter 由 Microsoft.Extensions.DependencyInjection.ObjectFactory 注入,我们不需要注册该过滤器。

1、ServiceFilter

//注入一个ServiceFilter
builder.Services.AddScoped<LoggingResponseHeaderFilterService>();
[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

2、TypeFilter

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

3.案例

services.AddControllers(c =>
{
    c.Filters.Add(typeof(ViewModelActionFiter));
    c.MaxModelValidationErrors = 99999;
}).ConfigureApiBehaviorOptions(o =>
{
    //.Net Core 禁用模型验证过滤器
    o.SuppressModelStateInvalidFilter = true;
})

在这里插入图片描述
ViewModelActionFiter相关代码

/// <summary>
/// ViewModelActionFiter
/// </summary>
public class ViewModelActionFiter : ActionFilterAttribute
{
    /// <summary>
    /// override OnActionExecuting
    /// </summary>
    /// <param name="context">context</param>
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        //模型校验失败处理的函数
        if (!context.ModelState.IsValid)
        {
            string method = context.HttpContext.Request.Method;
            ResultModel<object> result = new ResultModel<object>();
            StringBuilder msg = new StringBuilder();
            bool flag = false;
            foreach (var item in context.ModelState.Values)
            {
                if (method.Equals("GET"))
                {
                    msg.Append($",parameter value“{item.AttemptedValue}”does not pass the verification!");
                }
                else
                {
                    foreach (var error in item.Errors)
                    {
                        if (error.ErrorMessage.Contains("convert")
                            || error.ErrorMessage.Contains("Unexpected character")
                            || error.ErrorMessage.Contains("is not")
                            || error.ErrorMessage.Contains("valid")
                            || error.ErrorMessage.Contains("Input "))
                        {
                            flag = true;
                        }
                        else
                        {
                            msg.Append($",{error.ErrorMessage}");
                        }
                    }
                }
            }
            if (flag)
            {
                msg.Append($",The data is of incorrect type or the value exceeds the type range");
            }
            if (msg.ToString().Length > 0)
            {
                result.ErrorMessage += msg.ToString().Substring(1);
            }
            result.Code = 400;
            context.Result = new JsonResult(result, new Newtonsoft.Json.JsonSerializerSettings()
            {
                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
                Formatting = Newtonsoft.Json.Formatting.Indented,
                DateFormatString = "yyyy-MM-dd HH:mm:ss"
            });
        }
        else
        {
            base.OnActionExecuting(context);
        }

    }
}

在这里插入图片描述

二、中间件

注入中间件

app.UseMiddleware<ModernWMS.Core.Middleware.CorsMiddleware>();
app.UseMiddleware<GlobalExceptionMiddleware>();

在这里插入图片描述

1.跨域中间件

public class CorsMiddleware
{
    #region parameter
    /// <summary>
    /// agent
    /// </summary>
    private readonly RequestDelegate _next;
    #endregion

    #region Constructor

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="next">Delegate in next step</param>
    public CorsMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    #endregion

    /// <summary>
    /// Invoke
    /// </summary>
    /// <param name="httpContext">httpContext</param>
    /// <returns></returns>
    public Task Invoke(HttpContext httpContext)
    {

        if (httpContext.Request.Method == "OPTIONS")
        {
            httpContext.Response.Headers.Add("Access-Control-Allow-Origin", httpContext.Request.Headers["Origin"]);
            httpContext.Response.Headers.Add("Access-Control-Allow-Headers", httpContext.Request.Headers["Access-Control-Request-Headers"]);
            httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD,PATCH");
            httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
            httpContext.Response.Headers.Add("Access-Control-Max-Age", "86400");
            httpContext.Response.StatusCode = StatusCodes.Status200OK;
            return Task.CompletedTask;
        }
        if (httpContext.Request.Headers["Origin"] != "")
        {
            httpContext.Response.Headers.Add("Access-Control-Allow-Origin", httpContext.Request.Headers["Origin"]);
        }

        httpContext.Response.Headers.Add("Access-Control-Allow-Headers", httpContext.Request.Headers["Access-Control-Request-Headers"]);
        httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD,PATCH");
        httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
        httpContext.Response.Headers.Add("Access-Control-Max-Age", "86400");
        return _next.Invoke(httpContext);
    }

}

在这里插入图片描述

2.全局异常中间件

/// <summary>
/// Global exception middleware
/// </summary>
public class GlobalExceptionMiddleware
{
    #region parameter
    private readonly RequestDelegate next; 
    private readonly ILogger<GlobalExceptionMiddleware> logger;
    /// <summary>
    /// Localizer Service
    /// </summary>
    private readonly IStringLocalizer<MultiLanguage> _stringLocalizer;
    #endregion

    #region Constructor
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="next">Delegate in next step</param>
    /// <param name="logger">log manager</param>
    /// <param name="stringLocalizer">Localizer</param>
    public GlobalExceptionMiddleware(RequestDelegate next,
              ILogger<GlobalExceptionMiddleware> logger,
              IStringLocalizer<MultiLanguage> stringLocalizer
                                     )
    {
        this.next = next;
        this.logger = logger;
        this._stringLocalizer = stringLocalizer;
    }
    #endregion

    /// <summary>
    /// invoke
    /// </summary>
    /// <param name="context">httpcontext</param>
    /// <returns></returns>
    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next.Invoke(context);
        }
        catch (Exception ex)
        {
            await WriteExceptionAsync(context, ex);
        }
    }
    /// <summary>
    /// Write Log
    /// </summary>
    /// <param name="context">httpcontext</param>
    /// <param name="e">error messasge</param>
    /// <returns></returns>
    private async Task WriteExceptionAsync(HttpContext context, Exception e)
    {
        if (e != null)
        {
            var response = context.Response;
            var message = e.InnerException == null ? e.Message : e.InnerException.Message;
            response.ContentType = "application/json";

            var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = context.Connection.RemoteIpAddress.ToString();
            }
            logger.LogError($"\r\n\r\nIP:{ip},Exception:{e.Message}\r\nStackTrace:{e.StackTrace}");

            string result = Utility.JsonHelper.SerializeObject(ResultModel<object>.Error(_stringLocalizer["operation_failed"]));
            await context.Response.WriteAsync(result).ConfigureAwait(false);
        }
        else
        {
            var code = context.Response.StatusCode;
            switch (code)
            {
                case 200:
                    return;
                case 204:
                    return;
                case 401:
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsync(Utility.JsonHelper.SerializeObject(ResultModel<object>.Error("Invalid Token"))).ConfigureAwait(false);
                    break;
                default:
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsync(Utility.JsonHelper.SerializeObject(ResultModel<object>.Error("Unknown Error"))).ConfigureAwait(false);
                    break;
            }
        }
    }

}

在这里插入图片描述

三、格式化配置

.Net Core WebApi中输出格式几乎都是json,但是在core中使用任何服务都需要配置,这里需要配置 AddNewtonsoftJson

services.AddControllers()
        .AddNewtonsoftJson(options =>
          {
            //修改属性名称的序列化方式,首字母小写,即驼峰样式
            options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

            //日期类型默认格式化处理 方式1
            options.SerializerSettings.Converters.Add(new IsoDateTimeConverter(){DateTimeFormat = "yyyy/MM/dd HH:mm:ss"});
            //日期类型默认格式化处理 方式2
            options.SerializerSettings.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;
            options.SerializerSettings.DateFormatString = "yyyy/MM/dd HH:mm:ss";

            //忽略循环引用
            options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

            //解决命名不一致问题 
            options.SerializerSettings.ContractResolver = new DefaultContractResolver();
           
            //空值处理
            options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
});

可以参考微软官方文档:https://learn.microsoft.com/zh-cn/aspnet/core/web-api/advanced/formatting?view=aspnetcore-7.0

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。