【愚公系列】2023年02月 WMS智能仓储系统-010.全局过滤、中间件、格式化配置
【摘要】 前言本文主要讲解程序得全局配置,主要包含内容有全局过滤中间件格式化配置 一、全局过滤 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)