【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.pswp.cn/news/38724.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38724.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38724.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

go web框架 gin-gonic源碼解讀02————router

go web框架 gin-gonic源碼解讀02————router 本來想先寫context&#xff0c;但是發現context能簡單講講的東西不多&#xff0c;就準備直接和router合在一起講好了 router是web服務的路由&#xff0c;是指講來自客戶端的http請求與服務器端的處理邏輯或者資源相映射的機制。&…

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監聽器…

輕松解決docker容器啟動閃退

docker run -p 3306:3306 --name mysql8 \ -v /usr/local/mysql/log:/var/log/mysql \ -v /usr/local/mysql/data:/var/lib/mysql \ -v /usr/local/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD666 -d mysql:8.0.32執行這個命令的時候閃退&#xff0c;其實這個是命令是對你…

[cv] stable diffusion——2、公式

背景&#xff1a; 在圖像生成領域中&#xff0c;最常見的生成模型是GAN和VAE。然而&#xff0c;在2020年&#xff0c;提出了一種新的模型&#xff0c;即DDPM&#xff08;Denoising Diffusion Probabilistic Model&#xff09;&#xff0c;也被稱為擴散模型&#xff08;Diffusi…

基于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;那就是“交換機”。 速度不足的困境&…

CloudEvents—云原生事件規范

我們的系統中或多或少都會用到如下兩類業務技術&#xff1a; 異步任務&#xff0c;用于降低接口時延或削峰&#xff0c;提升用戶體驗&#xff0c;降低系統并發壓力&#xff1b;通知類RPC&#xff0c;用于微服務間狀態變更&#xff0c;用戶行為的聯動等場景&#xff1b; 以上兩種…

Go和Java實現解釋器模式

Go和Java實現解釋器模式 下面通過一個四則運算來說明解釋器模式的使用。 1、解釋器模式 解釋器模式提供了評估語言的語法或表達式的方式&#xff0c;它屬于行為型模式。這種模式實現了一個表達式接口&#xff0c;該接口 解釋一個特定的上下文。這種模式被用在 SQL 解析、符…

規劃性和可擴展性,助力企業全面預算管理的推進

對于當今社會經濟市場的不穩定狀況和不斷變化的消費者行為&#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與配…

寬度有限搜索BFS搜索數及B3625 迷宮尋路 P1451 求細胞數量 B3626 跳躍機器人

寬度有限搜索BFS搜索 B3625 迷宮尋路 題面 題目描述 機器貓被困在一個矩形迷宮里。 迷宮可以視為一個 nm 矩陣&#xff0c;每個位置要么是空地&#xff0c;要么是墻。機器貓只能從一個空地走到其上、下、左、右的空地。 機器貓初始時位于 (1,1) 的位置&#xff0c;問能否…

localhost:8080 is already in use

報錯原因&#xff1a;本機的8080端口號已經被占用。因為機器的空閑端口號是隨機分配的&#xff0c;而idea默認啟動的端口號是8080,所以是存在這種情況。 對于這個問題&#xff0c;我們只需要重啟idea或者修改項目的啟動端口號即可。 更推薦第二種。對于修改項目啟動端口號&…

Python 程序設計入門(020)—— 循環結構程序設計(1):for 循環

Python 程序設計入門&#xff08;020&#xff09;—— 循環結構程序設計&#xff08;1&#xff09;&#xff1a;for 循環 目錄 Python 程序設計入門&#xff08;020&#xff09;—— 循環結構程序設計&#xff08;1&#xff09;&#xff1a;for 循環一、for 循環的語法二、for …

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…