【NetCore】09-中间件

文章目录

  • 中间件:掌控请求处理过程的关键
    • 1. 中间件
      • 1.1 中间件工作原理
      • 1.2 中间件核心对象
    • 2.异常处理中间件:区分真异常和逻辑异常
      • 2.1 处理异常的方式
        • 2.1.1 日常错误处理--定义错误页的方法
        • 2.1.2 使用代理方法处理异常
        • 2.1.3 异常过滤器 IExceptionFilter
        • 2.1.4 特性过滤 ExceptionFilterAttribute
        • 2.1.5 异常处理技巧总结
    • 3 静态文件中间件: 前后端分离开发合并部署
      • 3.1 静态文件中间件的能力
      • 3.2 注册使用非www.root目录
    • 4 文件提供程序:将文件放在任何地方
      • 4.1 文件提供程序核心类型
      • 4.2 内置文件提供程序

中间件:掌控请求处理过程的关键

1. 中间件

1.1 中间件工作原理

中间件工作原理

1.2 中间件核心对象

  • IApplicationBuilder
  • RequestDelegate

IApplicationBuilder可以通过委托方式注册中间件,委托的入参也是委托,这就可以将这些委托注册成一个链,如上图所示;最终会调用Builder方法返回一个委托,这个委托就是把所有的中间件串起来后合并成的一个委托方法,Builder的委托入参是HttpContext(实际上所有的委托都是对HttpContext进行处理);
RequestDelegate是处理整个请求的委托。

中间件的执行顺序和注册顺序是相关的

//注册委托方式,注册自己逻辑// 对所有请求路径app.Use(async (context, next) =>{await next();await context.Response.WriteAsync("Hello2");});// 对特定路径指定中间件,对/abc路径进行中间件注册处理app.Map("/abc", abcBuilder =>{// Use表示注册一个完整的中间件,将next也注册进去abcBuilder.Use(async (context, next) =>{await next();await context.Response.WriteAsync("abcHello");});});// Map复杂判断,判断当前请求是否符合某种条件app.MapWhen(context =>{return context.Request.Query.Keys.Contains("abc");}, builder =>{// 使用Run表示这里就是中间件的执行末端,不再执行后续中间件builder.Run(async context =>{await context.Response.WriteAsync("new abc");});});

应用程序一旦开始向Response进行write时,后续的中间件就不能再操作Head,否则会报错
可以通过context.Resopnse.HasStarted方法判断head是否已经被操作

  • 设计自己的中间件
    中间件的设计时才有的约定的方式,即在方法中包含Invoke或者InvokeAsync,如下:
public class MyMiddleware{private readonly RequestDelegate next;private readonly ILogger<MyMiddleware> logger;public MyMiddleware(RequestDelegate next,ILogger<MyMiddleware> logger){this.next = next;this.logger = logger;}public async Task InvokeAsync(HttpContext context){using (logger.BeginScope("TraceIndentifier:{TraceIdentifier}",context.TraceIdentifier)){logger.LogDebug("Start");await next(context);logger.LogDebug("End");}}}public static class MyBuilderExtensions
{public  static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder app){return app.UseMiddleware<MyMiddleware>();}
}// 中间件使用
app.UseMyMiddleware();

2.异常处理中间件:区分真异常和逻辑异常

2.1 处理异常的方式

  • 异常处理页
  • 异常处理匿名委托方法
  • IExceptionFilter
  • ExceptionFilterAttribute
// startup中的Configureif (env.IsDevelopment()){app.UseDeveloperExceptionPage();// 开发环境下的异常页面,生产环境下是需要被关闭,页面如下图所示app.UseSwagger();app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LoggingSerilog v1"));
}

在这里插入图片描述

2.1.1 日常错误处理–定义错误页的方法

// startup中的Configure
app.UseExceptionHandler("/error");// 控制器
public class ErrprController : Controller
{[Route("/error")]public IActionResult Index(){// 获取上下文中的异常var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();var ex = exceptionHandlerPathFeature?.Error;var knowException = ex as IKnowException;if(knowException == null){var logger = HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();logger.LogError(ex,ex.Message);knowException = KnowException.Unknow;}else{knowException = KnowException.FromKnowException(knowException);}return View(knowException);}
}// 定义接口
public interface IKnowException
{public string Message {get;}public int ErrorCode {get;}public object[] ErrorData {get;}
}// 定义实现
public class KnowException : IKnowException
{public string Message {get;private set;}public int ErrorCode {get;private set;}public object[] ErrorData {get;private set;}public readonly static IKnowException Uknow = new KnowException{Message = "未知错误",ErrorCode = 9999};public static IKnowException FromKnowException(IKnowException exception){return new KnowException{Message = exception.Message,ErrorCode = exception.ErrorCode,ErrorData = exception.ErrorData};}
}// 需要定义一个错误页面 index.html,输出错误Message和ErrorCode

2.1.2 使用代理方法处理异常

// startup中的Configure
app.UseExceptionHandler(errApp =>
{errApp.Run(async context =>{var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();var ex = exceptionHandlerPathFeature?.Error;var knowException = ex as IKnowException;if(knowException == null){var logger = HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();logger.LogError(exceptionHandlerPathFeature.Error,exceptionHandlerPathFeature.Error.Message);knowException = KnowException.Unknow;context.Response.StatusCode = StatusCodes.Status500InternalServerError;}else{knowException = KnowException.FromKnowException(knowException);context.Response.StatusCode = StatusCodes.Status200OK;}var jsonOptions = context.RequestServices.GetService<Options<JsonOptions>>();context.Response.ContextType = "application/json";charset=utf-8";await context.Response.WriteAsync(System.Text.Json.JsonSerializer(knowException,jsonOptions.Value));});
});
  • 未知异常输出Http500响应,已知异常输出Http200
    因为监控系统会对Http响应码进行识别,如果返回的500比率比较高的时候,会认为系统的可用性有问题,告警系统会发出警告。对已知异常进行200响应能够让告警系统正常运行,能够正确识别系统一些未知的错误,使告警系统更加灵敏,避免了业务逻辑的异常干扰告警系统

2.1.3 异常过滤器 IExceptionFilter

异常过滤器是作用在整个MVC框架体系之下,在MVC整个声明周期中发生作用,也就是说它只能工作早MVC Web Api的请求周期里面

// 自定义异常过滤器
public class MyException : IExceptionFilter
{public void OnException(ExceptionContext context){IKnowException knowException = context.Exception as IKnowException;if(knowException == null){var loger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();logger.LogError(context.Exception,context.Exception.Message);knowException = KnowException.UnKnow;context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;}else{knowException = KnowException.FromKnowException(knowException);context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;}context.Result = new JsonResult(knowException){ContextType = "application/json:charset=utf-8"}}
}// startup注册
public void ConfigureServices(IServiceCollection services)
{services.AddMvc(mvcoption => {mvcOptions.Filters.Add<MyExceptionFilter>();}).AddJsonOptions(jsonOptions => {jsonOptions.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscapt});
}

2.1.4 特性过滤 ExceptionFilterAttribute

public class MyExceptionFilterAttriburte  : ExceptionFilterAttribute
{public override void OnException(ExceptionContext context){IKnowException knowException = context.Exception as IKnowException;if(knowException == null){var logger = context.HttpContext.RequestServices.GetServices<ILogger<MyExceptionFilterAttribute>>();logger.LogError(context.Exception,context.Exception.Message);knowException = KnowException.UnKnow;context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;}else{knowException = KnowException.FromKnowException(knowException);context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;}context.Result = new JsonResult(knowException){ContextType = "application/json:charset=utf-8"}}
}// 使用方式
在Controller控制器上方标注[MyExceptionFilter]
或者在 startup中ConfigureServices注册
services.AddMvc(mvcoption => {mvcOptions.Filters.Add<MyExceptionFilterAttribute>();});

2.1.5 异常处理技巧总结

  • 用特定的异常类或接口表示业务逻辑异常
  • 为业务逻辑异常定义全局错误码
  • 为未知异常定义定义特定的输出信息和错误码
  • 对于已知业务逻辑异常响应HTTP 200(监控系统友好)
  • 对于未预见的异常响应HTTP 500
  • 为所有的异常记录详细的日志

3 静态文件中间件: 前后端分离开发合并部署

3.1 静态文件中间件的能力

  • 支持指定相对路径
  • 支持目录浏览
  • 支持设置默认文档
  • 支持多目录映射
// startup的Configure方法中
app.UseDefaultFiles();// 设置默认访问根目录文件index.html
app.UseStaticFiles();// 将www.root目录映射出去

如果需要浏览文件目录,需要如下配置

// startup中的ConfigureServices中配置
services.AddDirectoryBrowser();// startup的Configure方法中
app.UseDirectoryBrowser();
app.UseStaticFiles();

3.2 注册使用非www.root目录

// startup的Configure方法中
app.UseStaticFiles();app.UseStaticFiles(new StaticFileOptions
{// 将程序中名为file文件目录注入RequestPath = "/files",// 设置文件指定访问路径,将文件目录映射为指定的Url地址FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(),"file"))
});

实际生产中会遇到将非api请求重定向到指定目录,采用如下配置

// startup的Configure方法中
app.MapWhen(context => 
{return !context.Request.Path.Value.StartWith("/api");
},appBuilder => 
{var option = new RewriteOptions();option.AddRewrite(".*","/index.html",true);appBuilder.UseRewriter(option);appBuilder.UseStaticFiles();
});

4 文件提供程序:将文件放在任何地方

4.1 文件提供程序核心类型

  • IFileProvider
  • IFileInfo
  • IDirectoryContexts

4.2 内置文件提供程序

  • PhysicalFileProvider⇒ 物理文件提供程序
  • EmbeddedFileProvider ⇒ 嵌入式文件提供程序
  • CompositeFileProvoder ⇒ 组合文件提供程序,将各种文件程序组合成一个目录
// 映射指定目录文件- 物理文件
IFileProvider provider1 = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory);
// 获取文件目录下内容
var contents = provider1.GetDirectoryContents("/");
// 输出文件信息
foreach (var item in contents)
{var stream = item.CreateReadStream();// 获取文件流Console.WriteLine(item.Name);
}// 嵌入式文件
IFileProvider fileProvider2 = new EmbeddedFileProvider(typeof(Program).Assembly);
var html = fileProvider2.GetFileInfo("file.html");// file.html文件属性设置为嵌入的资源// 组合文件提供程序
IFileProvider fileProvider3 = new CompositeFileProvider(provider1,fileProvider2);
var contexts3 = fileProvider3.GetDirectoryContents("/");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/38724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react实现对数组做增删改操作自定义hook

需求 实现对数组的增删改操作。 实现 import { useState } from react;const useArray (currList) > {const [list, setList] useState(currList);// 增const addItem (item) > {setList([...list, item]);};// 删const removeItem (idx) > {const _arr [...l…

实战指南,SpringBoot + Mybatis 如何对接多数据源

系列文章目录 MyBatis缓存原理 Mybatis plugin 的使用及原理 MyBatisSpringboot 启动到SQL执行全流程 数据库操作不再困难&#xff0c;MyBatis动态Sql标签解析 从零开始&#xff0c;手把手教你搭建Spring Boot后台工程并说明 Spring框架与SpringBoot的关联与区别 Spring监听器…

基于eBPF技术构建一种应用层网络管控解决方案

引言 随着网络应用的不断发展&#xff0c;在linux系统中对应用层网络管控的需求也日益增加&#xff0c;而传统的iptables、firewalld等工具难以针对应用层进行网络管控。因此需要一种创新的解决方案来提升网络应用的可管理性。 本文将探讨如何使用eBPF技术构建一种应用层网络…

【CSS】禁用元素鼠标事件(例如实现元素禁用效果)

文章目录 基本用法 基本用法 pointer-events 属性指定在什么情况下 (如果有) 某个特定的图形元素可以成为鼠标事件。实际运用中可以通过对auto 和none动态控制&#xff0c;来动态实现元素的禁用效果。 属性描述auto与pointer-events属性未指定时的表现效果相同&#xff0c;对…

【笔试题心得】排序算法总结整理

排序算法汇总 常用十大排序算法_calm_G的博客-CSDN博客 以下动图参考 十大经典排序算法 Python 版实现&#xff08;附动图演示&#xff09; - 知乎 冒泡排序 排序过程如下图所示&#xff1a; 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。对每一对相邻…

【LeetCode-简单】剑指 Offer 29. 顺时针打印矩阵(详解)

题目 输入一个矩阵&#xff0c;按照从外向里以顺时针的顺序依次打印出每一个数字。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;matrix [[1,2,3,4],[5,6,7,8],[9,10,1…

互联网发展历程:速度与效率,交换机的登场

互联网的演进就像一场追求速度与效率的竞赛&#xff0c;每一次的技术升级都为我们带来更快、更高效的网络体验。然而&#xff0c;在网络的初期阶段&#xff0c;人们面临着数据传输速度不够快的问题。一项关键的技术应运而生&#xff0c;那就是“交换机”。 速度不足的困境&…

规划性和可扩展性,助力企业全面预算管理的推进

对于当今社会经济市场的不稳定状况和不断变化的消费者行为&#xff0c;企业业务也从未像今天这样不可预测过。面对变化和变革&#xff0c;企业需要具备规划性的预测能力&#xff0c;才能使得自身在竞争中保持领先地位。那些具备前瞻性的企业都尝试在现阶段通过更好的规划不断提…

基于Mysqlrouter+MHA+keepalived实现高可用半同步 MySQL Cluster项目

目录 项目名称&#xff1a; 基于Mysqlrouter MHA keepalived实现半同步主从复制MySQL Cluster MySQL Cluster&#xff1a; 项目架构图&#xff1a; 项目环境&#xff1a; 项目环境安装包&#xff1a; 项目描述&#xff1a; 项目IP地址规划&#xff1a; 项目步骤: 一…

windows11下配置vscode中c/c++环境

本文默认已经下载且安装好vscode&#xff0c;主要是解决环境变量配置以及编译task、launch文件的问题。 自己尝试过许多博客&#xff0c;最后还是通过这种方法配置成功了。 Linux(ubuntu 20.04)配置vscode可以直接跳转到配置task、launch文件&#xff0c;不需要下载mingw与配…

localhost:8080 is already in use

报错原因&#xff1a;本机的8080端口号已经被占用。因为机器的空闲端口号是随机分配的&#xff0c;而idea默认启动的端口号是8080,所以是存在这种情况。 对于这个问题&#xff0c;我们只需要重启idea或者修改项目的启动端口号即可。 更推荐第二种。对于修改项目启动端口号&…

ZDH-wemock模块

本次介绍基于版本v5.1.1 目录 项目源码 预览地址 安装包下载地址 wemock模块 wemock模块前端 配置首页 配置mock wemock服务 下载地址 打包 运行 效果展示 项目源码 zdh_web: https://github.com/zhaoyachao/zdh_web zdh_mock: https://github.com/zhaoyachao/z…

TCGA数据下载推荐:R语言easyTCGA包

#使用easyTCGA获取数据 #清空 rm(listls()) gc() # 安装bioconductor上面的R包 options(BioC_mirror"https://mirrors.tuna.tsinghua.edu.cn/bioconductor") if(!require("BiocManager")) install.packages("BiocManager") if(!require("TC…

怎样让音频速度变慢?请跟随以下方法进行操作

怎样让音频速度变慢&#xff1f;在会议录音过程中&#xff0c;经常会遇到主讲人语速过快&#xff0c;导致我们无法清晰听到对方说的内容。如果我们能够减慢音频速度&#xff0c;就能更好地记录对方的讲话内容。此外&#xff0c;在听到快速播放的外语或方言时&#xff0c;我们也…

机器人的运动范围

声明 该系列文章仅仅展示个人的解题思路和分析过程&#xff0c;并非一定是优质题解&#xff0c;重要的是通过分析和解决问题能让我们逐渐熟练和成长&#xff0c;从新手到大佬离不开一个磨练的过程&#xff0c;加油&#xff01; 原题链接 机器人的运动范围https://leetcode.c…

高等数学教材重难点题型总结(二)导数与微分

本章重点题目较少&#xff0c;除了*标题页没什么特别难的&#xff0c;本帖出于总结性的角度考虑并未囊概全部的*标&#xff0c;最后会出一期*标题的全部内容整理&#xff0c;在攻克重难点的基础上更上一层楼。 1.根据定义求某点处的导数值 2.通过定义证明导数 3.左右导数的相关…

【数据库】P4 过滤数据 WHERE

过滤数据 WHERE 简介WHERE 子句操作符检测单个值案例范围值检查 BETWEEN AND空值检查 NULL 简介 数据库表一般包含大量的数据&#xff0c;很少需要检索表中的所有行。我们只检索所需数据需要指定搜索条件(search criteria)&#xff0c;搜索条件也称为过滤条件(filter conditio…

完全备份、增量备份、差异备份、binlog日志

Top NSD DBA DAY06 案例1&#xff1a;完全备份与恢复案例2&#xff1a;增量备份与恢复案例3&#xff1a;差异备份与恢复案例4&#xff1a;binlog日志 1 案例1&#xff1a;完全备份与恢复 1.1 问题 练习物理备份与恢复练习mysqldump备份与恢复 1.2 方案 在数据库服务器192…

问AI一个严肃的问题

chatgpt的问世再一次掀起了AI的浪潮&#xff0c;其实我一直在想&#xff0c;AI和人类的关系未来会怎样发展&#xff0c;我们未来会怎样和AI相处&#xff0c;AI真的会完全取代人类吗&#xff0c;带着这个问题&#xff0c;我问了下chatgpt&#xff0c;看一看它是怎么看待这个问题…

Modbus工业RFID设备在自动化生产线中的应用

传统半自动化生产线在运作的过程&#xff0c;因为技工的熟练程度&#xff0c;专业素养的不同&#xff0c;在制造过程中过多的人为干预&#xff0c;工厂将很难对每条生产线的产能进行标准化管理和优化。如果半自动化生产线系统是通过前道工序的作业结果和检测结果来决定产品在下…