一、为什么使用中间件
在我们很多时候,当一个请求过来之后,我们想对这个请求做各种各样的操作和记录,这个时候我们可以加入中间件
目的就是对这个请求和响应做处理,其实不难理解,这就是类似于工业机器,一个商品出来之前会有很多关卡,会执行N到工序,
最后加工出来的产品就是我们想要的,也是安全的。这些关卡就类似于中间件的作用了。
核心就是一系列的请求委托,Run、Use、Map
Run:是最后一道工序,管道末尾。
Use:连接请求委托,next 向下走。
Map:扩展用作约定创建管道分支。
自定义消息返回中间件
就是自定义消息返回值,在返回之前处理一下消息的格式和数据。(这个不建议使用,发布的时候会有fail,目前还没有时间查找,有时间了会回来解决)
首先在Model层创建一个ErrorModel.cs 返回格式 实体类,代码如下
using System; using System.Collections.Generic; using System.Text; namespace WebApi.Core.Model { /// <summary> /// 自定义返回消息实体类 /// </summary> public class ErrorModel { /// <summary> /// 状态码 /// </summary> public int code { get; set; } = 500; /// <summary> /// 错误信息 /// </summary> public string msg { get; set; } /// <summary> /// 错误详情 /// </summary> public string detail { get; set; } /// <summary> /// 时间戳 /// </summary> public string timestamp { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); } }
在api层增加一个文件夹Middleware 在新建一个中间件 CustomExceptionMiddleware
代码如下,注意如果复制的话,命名空间注意一下,也可以右键-新建项-选择中间件,只需要改一下就行。
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using WebApi.Core.Api.Log; using WebApi.Core.Model; namespace WebApi.Core.Api.Middleware { // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project public class CustomExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILoggerHelper _logger; public CustomExceptionMiddleware(RequestDelegate next, ILoggerHelper logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext httpContext) { try { await _next(httpContext); } catch (Exception ex) { _logger.Error(ex.Message, ex); // 日志记录 await HandleExceptionAsync(httpContext, ex.Message); } finally { var statusCode = httpContext.Response.StatusCode; var msg = ""; switch (statusCode) { case 401: msg = "未授权"; break; case 403: msg = "拒绝访问"; break; case 404: msg = "未找到服务"; break; case 405: msg = "405 Method Not Allowed"; break; case 502: msg = "请求错误"; break; } if (!string.IsNullOrWhiteSpace(msg)) { await HandleExceptionAsync(httpContext, msg); } } } private async Task HandleExceptionAsync(HttpContext httpContext, string msg) { ErrorModel error = new ErrorModel { code = httpContext.Response.StatusCode, msg = msg }; var result = JsonConvert.SerializeObject(error); httpContext.Response.ContentType = "application/json;charset=utf-8"; await httpContext.Response.WriteAsync(result).ConfigureAwait(false); } } // Extension method used to add the middleware to the HTTP request pipeline. public static class CustomExceptionMiddlewareExtensions { public static IApplicationBuilder UseCustomExceptionMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<CustomExceptionMiddleware>(); } } }
注册一下中间件
strartup.cs 中的Configure方法中 添加如下代码 顺序的话 放在权限处理(UseAuthentication)前面 就可以,不然请求从管道回来的时候会先走消息处理,然后在判断权限,这样的话就无法处理了。
app.UseCustomExceptionMiddleware();
接下来测试一下F5 可以看到 是没问题了。
我们现在处理了响应消息,
接下来我们做一个获取请求和响应的中间件
需要保存到日志中,查看都谁访问的返回的是什么
在middleware文件夹下创建一个中间件LogReqResponseMiddleware 代码如下
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using WebApi.Core.Api.Log; namespace WebApi.Core.Api.Middleware { // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project public class LogReqResponseMiddleware { private readonly RequestDelegate _next; private readonly ILoggerHelper _logger; public LogReqResponseMiddleware(RequestDelegate next, ILoggerHelper logger) { _next = next; _logger = logger; } public async Task Invoke(HttpContext httpContext) { var request = httpContext.Request; request.EnableBuffering(); //把请求body流转换成字符串 string bodyAsText = await new StreamReader(request.Body).ReadToEndAsync();//记录请求信息 var requestStr = $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}"; _logger.Info(typeof(LogReqResponseMiddleware), "Request:" + requestStr); request.Body.Seek(0, SeekOrigin.Begin); var originalBodyStream = httpContext.Response.Body; using (var responseBody = new MemoryStream()) { httpContext.Response.Body = responseBody; await _next(httpContext); var response = httpContext.Response; response.Body.Seek(0, SeekOrigin.Begin); //转化为字符串 string text = await new StreamReader(response.Body).ReadToEndAsync(); //从新设置偏移量0 response.Body.Seek(0, SeekOrigin.Begin); //记录返回值 var responsestr = $"{response.StatusCode}: {text}"; _logger.Info(typeof(LogReqResponseMiddleware),"Response:" + responsestr); await responseBody.CopyToAsync(originalBodyStream); } } } // Extension method used to add the middleware to the HTTP request pipeline. public static class LogReqResponseMiddlewareExtensions { public static IApplicationBuilder UseLogReqResponseMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<LogReqResponseMiddleware>(); } } }
然后在startup.cs 的configure注册一下,这里位置可以随意了,因为只是记录一下并没有对请求和响应做任何事情 代码如下
//记录请求和响应的json串 app.UseLogReqResponseMiddleware();
看一下效果 这里可以看到已经是记录进去了,报错是因为 我的redis没有开启。
今天写记录到这儿吧,后期遇到业务场景后,在慢慢添加吧