【NetCore】09-中间件

这篇具有很好参考价值的文章主要介绍了【NetCore】09-中间件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

1. 中间件

1.1 中间件工作原理

【NetCore】09-中间件,NetCore,中间件,.netcore,后端,开发语言

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中的Configure
 if (env.IsDevelopment())
 {
      app.UseDeveloperExceptionPage();// 开发环境下的异常页面,生产环境下是需要被关闭,页面如下图所示
      app.UseSwagger();
      app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LoggingSerilog v1"));
}

【NetCore】09-中间件,NetCore,中间件,.netcore,后端,开发语言

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请求重定向到指定目录,采用如下配置文章来源地址https://www.toymoban.com/news/detail-650747.html

// 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("/");

到了这里,关于【NetCore】09-中间件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 后端 .net7 Minimal API 限流中间件(微信小程序无师自通十)

            我的微信小程序使用.net7 Minimal API 作为后端,当服务器摆上公网后,可以观察到很多的攻击行为和暴力访问。所以,我需要使用微软的限流中间件部署相应的功能在服务器上 : AddFixedWindowLimiter using Microsoft.AspNetCore.RateLimiting; using System.Threading.RateLimiting; 后端

    2024年02月12日
    浏览(29)
  • Linux中间件开发

    Linux中间件开发通常是指开发运行在Linux操作系统上的中间件软件,这些软件通常用于连接不同的应用程序或平台,提供服务的支持和协调。常见的Linux中间件包括Web服务器、应用服务器、消息队列、数据库中间件等。 中间件开发的主要任务是实现中间件的核心功能,例如We

    2024年02月12日
    浏览(32)
  • 【开发】中间件——ElasticSearch

    ElasticSearch是一个基于Lucene的搜索服务器。提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口 ElasticSearch是一个基于Lucene的搜索服务器。提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口 ElasticSearch是用JAVA开发的。达到实时搜索,稳定可靠,快速,

    2024年02月17日
    浏览(44)
  • node 第十四天 基于express的第三方中间件multer node后端处理用户上传文件

    Multer 是一个 node.js 中间件,用于处理 multipart/form-data 类型的表单数据,它主要用于上传文件。它是写在 busboy 之上的所以非常高效。 前面我们已经知道了怎样利用express提供的静态资源处理中间件 express.static() 处理用户请求静态资源文件(图片, js, css等) 接下来学习如何处理用

    2024年02月06日
    浏览(33)
  • 开发实践6_缓存^中间件

    以下学习 朔宁夫 开发工程师 课程。 缓存可提高程序响应速度。数据库缓存(可过期)/ Redis缓存(Key:Value)/ Memcacheed缓存/ 程序层缓存。 一 缓存 1. 数据库缓存 创建缓存数据表 // python manage.py createcachetable cache_table setting // 创建新的演示 app //  python manage.py startapp cache_app setting注册

    2024年01月19日
    浏览(27)
  • 使用开源中间件:云计算环境中的中间件采用开源框架,可以减少开发和部署时间,提高开发效率和可靠性。

    作者:禅与计算机程序设计艺术 云计算是一个新的分布式计算模型,具有独特的特征。它把资源的虚拟化、弹性伸缩和按需付费等能力集成到了一起。这种新型的分布式计算模式,使得软件工程师们在编写应用程序时无须担心服务器性能,而只需要关注应用本身。同时,云平

    2024年02月04日
    浏览(43)
  • Java开发框架和中间件面试题(8)

    目录 82.Mybatis一级缓存,二级缓存? 83.Mybatis如何防止SQL注入? 84.mybatis中resultType和resultMap有什么区别? 85.如何在SpringBoot中禁用Actuator断点安全性? 86.什么是SpringBoot?SpringBoot有哪些优点? 87.SpringBoot中的监视器是什么? 88.什么是yaml文件? 89.如何使用SpringBoot实现异常处理?

    2024年02月03日
    浏览(39)
  • 【云开发笔记NO.24】中间件和中台

    在云开发领域,中间件是一个至关重要的概念,它扮演着连接不同技术组件和应用程序的桥梁角色。中间件,顾名思义,是处于两个应用程序或系统之间的软件层,其主要负责屏蔽底层操作系统的复杂性,为上层应用程序提供一个统一、简化的接口。这种软件的存在大大减轻

    2024年04月11日
    浏览(32)
  • 开源国内镜像站 操作系统、中间件、开发环境

    ​ 1.搜狐:Index of / 2.网易:欢迎访问网易开源镜像站 3.阿里云:阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 4.腾讯:http://android-mirror.bugly.qq.com:8080/(仅针对APP开发的软件,限流,不推荐) 1.上海交通大学:http://ftp.sjtu.edu.cn/html/resources.xml(部分移动运营商出口状况不佳

    2023年04月09日
    浏览(37)
  • 麦芯(MachCore)应用开发教程1 --- 设备软件中间件

    黄国强 2024/1/10 acloud@163.com         对任何公司来说,在短时间内开发一款高质量设备专用软件,是一件不太容易做到的事情。麦芯是笔者发明的一款设备软件中间件产品。麦芯致力于给设备厂商提供一个开发工具和平台,让客户快速高效的开发自己的设备专用软件。麦芯

    2024年01月25日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包