中间件是一种装配到应用管道以处理请求和响应的软件。 每个组件:

  • 选择是否将请求传递到管道中的下一个组件。
  • 可在管道中的下一个组件前后执行工作。

请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。

使用 RunMap 和 Use 扩展方法来配置请求委托。 可将一个单独的请求委托并行指定为匿名方法(称为并行中间件),或在可重用的类中对其进行定义。 这些可重用的类和并行匿名方法即为中间件,也叫中间件组件。 请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。 当中间件短路时,它被称为“终端中间件”,因为它阻止中间件进一步处理请求。

将 HTTP 处理程序和模块迁移到 ASP.NET Core 中间件介绍了 ASP.NET Core 和 ASP.NET 4.x 中请求管道之间的差异,并提供了更多的中间件示例。

中间件顺序

 向 Startup.Configure 方法添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此顺序对于安全性、性能和功能至关重要。

 // 运行时调用此方法。使用此方法配置HTTP请求管道。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} // 启用swagger中间件
app.UseSwaggerMiddleware(); // 全局异常捕获
app.UseErrorHandlingMiddleware(); //静态文件
app.UseUploadConfig(); // 路由中间件
app.UseRouting(); // 跨域检查
app.UseCors(_allowSpecificOrigins); // 启用多租户中间件(自定义)
app.UseMultiTenant(); // (认证中间件)启用Authentication中间件,遍历策略中的身份验证方案获取多张证件,最后合并放入HttpContext.User中
app.UseAuthentication(); // (授权中间件)对请求进行权限验证
app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
//endpoints.MapControllerRoute("default", "{__tenant__=tenant1}/api/{controller=Home}/{action=Index}/{id?}");
//endpoints.MapControllerRoute("default", "{__tenant__=}/api/{controller=Home}/{action=Index}");
});
}

在上述代码中:

  • 在使用单个用户帐户创建新的 Web 应用时未添加的中间件已被注释掉。
  • 并非所有中间件都需要准确按照此顺序运行,但许多中间件必须遵循这个顺序。 例如:
    • UseCorsUseAuthentication 和 UseAuthorization 必须按照上述顺序运行。
    • 由于此错误UseCors 当前必须在 UseResponseCaching 之前运行。

以下 Startup.Configure 方法将为常见应用方案添加中间件组件:

  1. 异常/错误处理

    • 当应用在开发环境中运行时:

      • 开发人员异常页中间件 (UseDeveloperExceptionPage) 报告应用运行时错误。
      • 数据库错误页中间件报告数据库运行时错误。
    • 当应用在生产环境中运行时:
      • 异常处理程序中间件 (UseExceptionHandler) 捕获以下中间件中引发的异常。
      • HTTP 严格传输安全协议 (HSTS) 中间件 (UseHsts) 添加 Strict-Transport-Security 标头。
  2. HTTPS 重定向中间件 (UseHttpsRedirection) 将 HTTP 请求重定向到 HTTPS。
  3. 静态文件中间件 (UseStaticFiles) 返回静态文件,并简化进一步请求处理。
  4. Cookie 策略中间件 (UseCookiePolicy) 使应用符合欧盟一般数据保护条例 (GDPR) 规定。
  5. 用于路由请求的路由中间件 (UseRouting)。
  6. 身份验证中间件 (UseAuthentication) 尝试对用户进行身份验证,然后才会允许用户访问安全资源。
  7. 用于授权用户访问安全资源的授权中间件 (UseAuthorization)。
  8. 会话中间件 (UseSession) 建立和维护会话状态。 如果应用使用会话状态,请在 Cookie 策略中间件之后和 MVC 中间件之前调用会话中间件。
  9. 用于将 Razor Pages 终结点添加到请求管道的终结点路由中间件(带有 MapRazorPages 的 UseEndpoints)。

内置中间件

ASP.NET Core 附带以下中间件组件。 “顺序”列提供备注,以说明中间件在请求处理管道中的放置,以及中间件可能会终止请求处理的条件。 如果中间件让请求处理管道短路,并阻止下游中间件进一步处理请求,它被称为“终端中间件”。 若要详细了解短路,请参阅使用 IApplicationBuilder 创建中间件管道部分。

 自定义中间件  比如异常中间件

首先,创建一个中间件ExceptionMiddleware

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace NetLock.Presentation.Api.Middleware
{
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next; private readonly ILogger<ErrorHandlingMiddleware> _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
this.next = next;
this._logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
} } private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
//var code = HttpStatusCode.InternalServerError; // 500 if unexpected
var code = StatusCodes.Status500InternalServerError;
string info = "服务器内部错误,无法完成请求";
if (ex is Exception)
{
code = 401;
info = ex.Message == "" ? "未登录" : ex.Message; }
else if (ex is UnAuthorizeException)
{
code = 401;
info = ex.Message == "" ? "无权访问" : ex.Message;
}
else
{
switch (context.Response.StatusCode)
{
case 401:
info = "没有权限";
break;
case 404:
info = "未找到服务";
break;
case 403:
info = "服务器理解请求客户端的请求,但是拒绝执行此请求";
break;
case 500:
info = "服务器内部错误,无法完成请求";
break;
case 502:
info = "请求错误";
break;
default:
info = ex.Message;
break;
} } _logger.LogError(info); // todo:可记录日志,如通过注入Nlog等三方日志组件记录 var result = JsonConvert.SerializeObject(new { Coede= code.ToString(), Message = info });
context.Response.ContentType = "application/json";
context.Response.StatusCode = code;
return context.Response.WriteAsync(result);
}
}
}

管道的添加顺序决定了它的执行顺序,所以如果您想扩大异常捕获的范围,可以将该管道放置在 Configure 的第一行。 但是!! 您会发现,这个默认的AspNet Core项目不是已经在第一行弄了一个异常处理么?

if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}

这行代码大家在初始化新AspNetCore项目时就会看到,也有可能您只有上半段,这和模板有关系。不过这都没有关系,它的作用就是捕获和处理异常而已。关于 UseDeveloperExceptionPage 该扩展咱们就不多说了,它的意思是:对于开发模式,一旦报错就会跳转到错误堆栈页面。 而第二个 UseExceptionHandler 就很有意思了,从它命名就可以看出,它肯定是个错误拦截程序。那么它和咱们自定义的异常处理管道有什么区别呢?

“不指定肯定有个默认吧!” 是的,它就是默认的错误处理。所以,它其实也是一个中间件,它的真身叫做 ExceptionHandlerMiddleware。在使用 UseExceptionHandler 方法时,我们可以选填各种参数。比如上方的代码,填入了 "/Error" 参数,表示当产生异常的时候,将定向到对应路径,此处就定位的是: “http://localhost:5001/Error” 。当然您也可以随意指定页面,比如 漂亮的乔殿下页面。

创建 HandleException(HttpContext context, Exception e) 处理异常,判断是 Development 环境下,输出详细的错误信息,非 Development 环境仅提示调用者“抱歉,出错了”,同样,在 Startup.cs 中将 ExceptionMiddleware 加入管道中

//ExceptionMiddleware 加入管道
app.UseMiddleware<ExceptionMiddleware>();

通过依赖注入和管道中间件两种不同的全局捕获异常处理。实际项目中,也是应当区分不同的业务场景,输出不同的日志信息,不管是从安全或者是用户体验友好性上面来说,都是非常值得推荐的方式,全局异常捕获处理,完全和业务剥离。

IExceptionFilter 作为MVC中间件之间的内容,它需要MVC在发现错误之后将错误信息提交给它处理,因此它的错误处理范围仅限于MVC中间件。所以,假如我们需要捕获MVC中间件之前的一些错误,其实是捕获不到的。 而对于ExceptionHandlerMiddleware中间件来说就很简单了,它作为第一个中间件,凡是在它之后的所有错误它都能够捕获得到。

那么这么看来是否IExceptionFilter就毫无用武之地了呢? 非也,假如您想在MVC发生异常时快速捕获和处理,使用过滤器其实是您不错得选择,如果您仅仅关心控制器之间的异常,那么过滤器也是很好的选择。

还记得刚开始我们在过滤器中说过的这一行代码吗:context.ExceptionHandled = true;。如果在IExceptionFilter中将异常标记为已经处理之后,则第一道异常处理中间件就认为没有错误了,不会进入到处理逻辑中。所以,如果咱们不把该属性改为 true,很有可能出现拦截结果被覆盖的情况。

ASP.NET Core 中间件 自定义全局异常中间件以及 MVC异常过滤器作用的更多相关文章

  1. Asp.Net Core 第06局:中间件

    总目录 前言 本文介绍Asp.Net Core 中间件. 环境 1.Visual Studio 2017 2.Asp.Net Core 2.2 开局 第一手:中间件概述     1.中间件:添加到应用 ...

  2. 如何在ASP.NET Core中自定义Azure Storage File Provider

    文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...

  3. ASP.NET Core Identity自定义数据库结构和完全使用Dapper而非EntityFramework Core

    前言 原本本节内容是不存在的,出于有几个人问到了我:我想使用ASP.NET Core Identity,但是我又不想使用默认生成的数据库表,想自定义一套,我想要使用ASP.NE Core Identi ...

  4. ASP.NET Core中,UseDeveloperExceptionPage扩展方法会吃掉异常

    在ASP.NET Core中Startup类的Configure方法中,有一个扩展方法叫UseDeveloperExceptionPage,如下所示: // This method gets call ...

  5. 干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结

    目录 C# VS JAVA 基础语法类比篇: 一.匿名类 二.类型初始化 三.委托(方法引用) 四.Lambda表达式 五.泛型 六.自动释放 七.重写(override) ASP.NET CORE ...

  6. ASP.NET CORE(C#)与Spring Boot MVC(JAVA)

    干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结   目录 C# VS JAVA 基础语法类比篇: 一.匿名类 二.类型初始化 三.委托(方 ...

  7. 从ASP.Net Core Web Api模板中移除MVC Razor依赖项

    前言 :本篇文章,我将会介绍如何在不包括MVC / Razor功能和包的情况下,添加最少的依赖项到ASP.NET Core Web API项目中. 一.MVC   VS WebApi (1)在ASP. ...

  8. ASP.NET Core 中间件自定义全局异常处理

    目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...

  9. Asp.Net Core 通过自定义中间件防止图片盗链的实例(转)

    一.原理 要实现防盗链,我们就必须先理解盗链的实现原理,提到防盗链的实现原理就不得不从HTTP协议说起,在HTTP协议中,有一个表头字段叫referer,采用URL的格式来表示从哪儿链接到当前的网页或 ...

随机推荐

  1. kafka 数据存储和发送

    摘要 前面我们已经解释获取和更新metadata以及重要性,那么如何给topic 发送数据? kafkaclient和broker通信,有很多种情况,核心的broker提供的接口有6个 元数据接口(M ...

  2. IPv6 Rapid Deployment, IPv6 6rd初探

    IPv6 Rapid Deployment: Provide IPv6 Access to Customers over an IPv4-Only Network 原文地址:https://www.c ...

  3. Less从入门到精通——变量与混合

    变量(variables) Less中采用" @ "定义变量,使用时直接使用"@命名符". 作为普通变量 // 命名: @color:red; // 调用: d ...

  4. Cloud-Native! 实战 Helm 3 部署 Traefik 2

    介绍 Traefik 是什么? Traefik, The Cloud Native Edge Router Traefik 是一种现代 HTTP 反向代理和负载均衡器,用于轻松部署微服务. 这篇文章对 ...

  5. Alpha冲刺-第三次冲刺笔记

    Alpha冲刺-冲刺笔记 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 这个作业要求在哪里 https://edu.cnblogs. ...

  6. moviepy音视频开发:音频文件存取类AudioFileClip属性和方法介绍

    ☞ ░ 前往老猿Python博文目录 ░ 一.概述 AudioFileClip是AudioClip的直接子类,用于从一个音频文件或音频数组中读入音频到内存构建音频剪辑.但AudioFileClip并不 ...

  7. PyQt学习随笔:Model/View开发时在view数据项中设置不同角色数据的方法

    在往Model中通过QStandardItem等类插入数据项时,除了实际插入的存储数据,还可以设置不同角色(请参考<PyQt学习随笔:Model/View中诸如DisplayRole的数据角色及 ...

  8. 本地web项目部署到服务器里连接不上数据库的解决办法

    今天突然想到把自己之前的项目挂到服务器上,但是用到了数据库,于是给服务器装上了MySQL,想着能赶紧把项目挂上去看看效果,然后并不是一帆风顺,在奋斗了四小时后终于解决了问题的所在. (1)首先我找到了 ...

  9. linux常用快捷键总结

    启动器:<super> 显示桌面:<super>D 文件管理器:<super>E 显示工作区:<super>S 打开终端:ctrl+alt+T 关闭窗口 ...

  10. [SQL Server]多次为 '派生表' 指定了列 'id'

    问题: 原因: 因为派生表oo中出现了两个同样的'ID'属性,所以会报[多次为 'o' 指定了列 'ID']的错误. 只需要把第二个星号替换成所需要的列名并把重复字段重命名就好了 解决方案: