【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.rhkb.cn/news/97129.html

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

相关文章

2023国赛数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

图数据库_Neo4j学习cypher语言_常用函数_关系函数_字符串函数_聚合函数_数据库备份_数据库恢复---Neo4j图数据库工作笔记0008

然后再来看一些常用函数,和字符串函数,这里举个例子,然后其他的 类似 可以看到substring字符串截取函数 可以看到截取成功 聚合函数 这里用了一个count(n) 统计函数,可以看到效果 关系函数,我们用过就是id(r) 可以取出对应的r的id来这样..

【不带权重的TOPSIS模型详解】——数学建模

目录索引 定义&#xff1a;问题引入&#xff1a;不合理之处&#xff1a;进行修改&#xff1a; 指标分类&#xff1a;指标正向化&#xff1a;极小型指标正向化公式&#xff1a;中间型指标正向化公式&#xff1a;区间型指标正向化公式&#xff1a; 标准化处理(消去单位)&#xff…

【应用笔记】使用 CW32 实现电池备份(VBAT)功能

前言 电池备份&#xff08;VBAT&#xff09;功能的实现方法&#xff0c;一般是使用 MCU 自带的 VBAT 引脚&#xff0c;通过在该引脚连接钮扣电池&#xff0c;当系统电源因故掉电时&#xff0c;保持 MCU 内部备份寄存器内容和 RTC 时间信息不会丢失。 本文档介绍了如何基于 C…

PHP8的正则表达式-PHP8知识详解

在网页程序的时候&#xff0c;经常会有查找符合某些复杂规则的字符串的需求。正则表达式就是描述这些规则的工具。 正则表达式是把文本或者字符串按照一定的规范或模型表示的方法&#xff0c;经常用于文本的匹配操作。 例如&#xff1a;我们在填写手机号码的时候&#xff0c;…

java-JVM 类加载机制

JVM 类加载机制 JVM 类加载机制分为五个部分&#xff1a;加载&#xff0c;验证&#xff0c;准备&#xff0c;解析&#xff0c;初始化&#xff0c;下面我们就分别来看一下这五个过程。 1.1. 加载 加载是类加载过程中的一个阶段&#xff0c;这个阶段会在内存中生成一个代表这…

windows vscode使用opencv

1.windows vscode使用opencv 参考&#xff1a;https://blog.csdn.net/zhaiax672/article/details/88971248 https://zhuanlan.zhihu.com/p/402378383 https://blog.csdn.net/weixin_39488566/article/details/121297536 g -g .\hello_opencv.cpp -stdc14 -I E:\C-software\…

前端 -- 基础 网页、HTML、 WEB标准 扫盲详解

什么是网页 : 网页是构成网站的基本元素&#xff0c;它通常由 图片、链接、文字、声音、视频等元素组成。 通常我们看到的网页 &#xff0c;常见以 .html 或 .htm 后缀结尾的文件&#xff0c; 因此俗称 HTML 文件 什么是 HTML : HTML 指的是 超文本标记语言&#xff0c…

java面试基础 -- 普通类 抽象类 接口

目录 抽象类语法 抽象类特性 普通类 & 抽象类 抽象类 & 接口 什么是接口 语法 接口方法 变量 接口特性 抽象类&接口的区别 抽象类语法 在Java中&#xff0c;一个类如果被 abstract 修饰称为抽象类&#xff0c;抽象类中被 abstract 修饰的方法称为抽象…

Android 场景Scene的使用

Scene 翻译过来是场景&#xff0c;开发者提供起始布局和结束布局&#xff0c;就可以实现布局之间的过渡动画。 具体可参考 使用过渡为布局变化添加动画效果 大白话&#xff0c;在 Activity 的各个页面之间切换&#xff0c;会带有过渡动画。 打个比方&#xff0c;使用起来类似…

vscode如何汉化

首先我们到vscode官网下载 链接如下&#xff1a; Visual Studio Code - Code Editing. Redefined 根据自己需要的版本下载就好 下载并且安装完毕之后 运行vscode 然后按快捷键 CTRLSHIFTX 打开安装扩展界面 搜索简体中文 安装就可以了 谢谢大家观看

Hadoop小结(上)

最近在学大模型的分布式训练和存储&#xff0c;自己的分布式相关基础比较薄弱&#xff0c;基于深度学习的一切架构皆来源于传统&#xff0c;我总结了之前大数据的分布式解决方案即Hadoop&#xff1a; Why Hadoop Hadoop 的作用非常简单&#xff0c;就是在多计算机集群环境中营…

【C++11新特性】lambda表达式

文章目录 1. lambda表达式概念2. lambda表达式语法3. lambda表达式应用 1. lambda表达式概念 lambda表达式是一个匿名函数&#xff0c;恰当使用lambda表达式可以让代码变得简洁&#xff0c;并且可以提高代码的可读性。 见见lambda表达式的使用 现在要对若干商品分别按照价格和…

十六、Spring Cloud Sleuth 分布式请求链路追踪

目录 一、概述1、为什么出出现这个技术&#xff1f;需要解决哪些问题2、是什么&#xff1f;3、解决 二、搭建链路监控步骤1、下载运行zipkin2、服务提供者3、服务调用者4、测试 一、概述 1、为什么出出现这个技术&#xff1f;需要解决哪些问题 2、是什么&#xff1f; 官网&am…

Linux0.11内核源码解析-truncate.c

truncate文件只要实现释放指定i节点在设备上占用的所有逻辑块&#xff0c;包括直接块、一次间接块、二次间接块。从而将文件节点对应的文件长度截为0&#xff0c;并释放占用的设备空间。 索引节点的逻辑块连接方式 释放一次间接块 static void free_ind(int dev,int block) {…

Windows10上VS2022单步调试FFmpeg 4.2源码

之前在 https://blog.csdn.net/fengbingchun/article/details/103735560 介绍过通过VS2017单步调试FFmpeg源码的方法&#xff0c;这里在Windows10上通过VS2022单步调试FFmpeg 4.2的方法&#xff1a;基于GitHub上ShiftMediaProject/FFmpeg项目&#xff0c;下面对编译过程进行说明…

【mysql】—— 表的增删改查

目录 序言 &#xff08;一&#xff09;Create操作 1、单行数据 全列插入 2、多行数据 指定列插入 3、插入否则更新 4、直接替换 &#xff08;二&#xff09;Retrieve操作 1、SELECT 列 1️⃣全列查询 2️⃣指定列查询 3️⃣查询字段为表达式 4️⃣为查询结果指定…

snpEff变异注释的一点感想

snpEff变异注释整成人生思考 1.介绍2.安装过程以及构建物种参考数据库3.坑货来了4.结果文件判读5.小tips 1.介绍 &nbsp SnpEff&#xff08;Snp Effect&#xff09;是一个用于预测基因组变异&#xff08;例如单核苷酸变异、插入、缺失等&#xff09;对基因功能的影响的生物…

aardio简单网站css或js下载练习

import win.ui; /*DSG{{*/ var winform win.form(text"下载网站css或js";right664;bottom290;maxfalse) winform.add( buttonClose{cls"button";text"退出";left348;top204;right498;bottom262;color14120960;fontLOGFONT(h-14);note" &qu…

如何使用Redis实现附近商家查询

导读 在日常生活中&#xff0c;我们经常能看见查询附近商家的功能。 常见的场景有&#xff0c;比如你在点外卖的时候&#xff0c;就可能需要按照距离查询附近几百米或者几公里的商家。 本文将介绍如何使用Redis实现按照距离查询附近商户的功能&#xff0c;并以SpringBoot项目…