一、什么是中间件?
中间件是一种装配到应用管道以处理请求和响应的软件。是介于request与response处理过程之间的一个插件(一道处理过程),相对比较轻量级,并且在全局上会影响到request对象和response对象的属性。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。每个组件:
1、选择是否将请求传递到管道中的下一个组件。
2、可在管道中的下一个组件前后执行工作。
原理图:
多个中间件时,中间件请求和响应的中间件顺序相反
二、为什么使用中间件?
在我们很多时候,当一个请求过来之后,我们想对这个请求做各种各样的操作和记录,这个时候我们可以加入中间件。目的就是对这个请求和响应做处理,其实不难理解,这就是类似于工业机器,一个商品出来之前会有很多关卡,会执行N到工序。最后加工出来的产品就是我们想要的,也是安全的。这些关卡就类似于中间件的作用了。
微软约定中间件需要两个参数,一个是httpcontext上下文对象,一个是Task类型的委托。通过上下文对象,处理请求,通过委托传递上下文对象到下一个中间件。核心就是一系列的请求委托,Run、Use、Map
-
Run:是最后一道工序,管道末尾。
-
Use:连接请求委托,next 向下走。
-
Map:扩展用作约定创建管道分支。
三、定义中间件:
中间件的处理流程就像一个俄罗斯套娃,微软约定中间件需要两个参数,一个是httpcontext上下文对象,一个是Task类型的委托。通过上下文对象,处理请求,通过委托传递上下文对象到下一个中间件,这也是套娃模式的由来。RequestDelegate是管道的核心。ApplicationBuilder就是接收了很多个RequestDelegae把它拼到一起。
定义:
/// <summary>
/// 中间件定义和业务逻辑
/// </summary>
public class MyMiddleware
{
private readonly RequestDelegate _next;
/// <summary>
/// 构造
/// </summary>
/// <param name="next"></param>
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
/// <summary>
/// 方法名必须命名为 Invoke或者 InvokeAsync,才能有效执行下一个中间件
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public async Task InvokeAsync(HttpContext httpContext)
{
/*
* 在这里可以书写业务处理逻辑
*中间件的处理流程就像一个俄罗斯套娃,微软约定中间件需要两个参数,一个是httpcontext上下文对象,一个是Task类型的委托。
* 通过上下文对象,处理请求,通过委托传递上下文对象到下一个中间件(这也是套娃模式的由来)。
*/
try
{
await _next(httpContext);
}
catch (Exception ex)
{
//内部出现异常
httpContext.Response.StatusCode = 500;
}
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 500:
msg = "服务器内部错误";
break;
case 502:
msg = "请求错误";
break;
}
if (!string.IsNullOrWhiteSpace(msg))
{
await HandleExceptionAsync(httpContext, msg);
}
}
}
/// <summary>
/// 处理Http响应异常
/// </summary>
/// <param name="httpContext"></param>
/// <param name="msg"></param>
/// <returns></returns>
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);
}
}
封装拓展方法:
创建一个中间件拓展类,为每个自定义中间件创建方法,通过IApplicationBuilder拓展方法暴露
/// <summary>
/// 中间件拓展类
/// </summary>
public static class MyMiddlewareExtensions
{
/// <summary>
/// 将封装的中间件委托到一个类中,通过IApplicationBuilder拓展方法暴露
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IApplicationBuilder UseMyMiddlewareOne(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
/*
* 下面还可以拓展其他自定义中间件方法,通过IApplicationBuilder暴露
*/
}
四、配置使用中间件:
使用中间件:
注意:使用中间件,顺序非常重要。比如此处,要放在权限处理的前面。不然请求从管道回来的时候,会先走消息处理,然后再判断权限,这样的话就无法处理了。因为使用多个中间件时,中间件请求和响应的顺序是相反的,此处还是爬楼看上面的原理图比较清晰。
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//开发环境使用
app.UseSwagger();
app.UseSwaggerUI(option =>
{
foreach (string version in typeof(ApiVersions).GetEnumNames())
{
option.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"版本:{version}");
}
});
}
app.UseRouting();
//使用自定义中间件:
app.UseMyMiddlewareOne();//注册自定义中间件
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
五、演示:
定义测试Http接口:
/// <summary>
/// 自定义中间件测试
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
[HttpGet]
public int MiddleWareTest(string param)
{
/*
* note:此处的字符串是否是数字不做判断,当输入的非数字字符串时,强转Int服务内部会
*/
int Number = int.Parse(param);
return Number;
}