首页 > 编程语言 >.net 温故知新【17】:Asp.Net Core WebAPI 中间件

.net 温故知新【17】:Asp.Net Core WebAPI 中间件

时间:2024-01-18 09:04:32浏览次数:31  
标签:WebAPI Use 温故知新 app await 中间件 context Response

一、前言

到这篇文章为止,关于.NET "温故知新"系列的基础知识就完结了,从这一系列的系统回顾和再学习,对于.NET core、ASP.NET CORE又有了一个新的认识。

不光是从使用,还包括这些知识点的原理,虽然深入原理谈不上,但对于日常使用也够了,我想的是知其然,知其所以然。

在实际开发过程中可能是知道怎么使用就行,但系统学习了这些基本的框架、组件、或者说原理后,对于我们软件设计、开发、扩展和解决问题还是有帮助的。

刚好到2023新年前赶着写完,也算对自己这个系列的一个交代,实际上我平时基本不使用ASP.NET CORE,目前我主要开发桌面程序,还是用的winform。

写这个系列的初衷是想紧跟.NET的发展进程,同时储备基础知识,平时还搞一些微服务(Java)、NLP、OCR、知识图谱、前端(Vue3),只要需要反正啥都搞,没必要固执,技术只是手段,不是目的。

那么接下来就继续简单的梳理一下中间件,欢迎对这个系列拍砖!

二、中间件

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

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

这个是关于中间件概念的概括,官方的概括是相当精准,那么我们就围绕管道、传递、组件来看看中间件。

请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。使用 Run、Map 和 Use 扩展方法来配置请求委托。

我们照例新建一个ASP.NET CORE Web API 项目:WebAPI_Middleware

namespace WebAPI_Middleware
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();

            app.UseAuthorization();


            app.MapControllers();

            app.Run();
        }
    }
}

在Program.cs 中我们看到前面部分builder是配置依赖注入的东西,这部分可以参看.net 温故知新【13】:Asp.Net Core WebAPI 使用依赖注入DI

app 使用Use扩展用于中间件添加到管道中

Map 基于给定请求路径的匹配项来创建请求管道分支

Run 委托始终为终端,用于终止管道。

中间件的执行顺序过程如下:

image

三、Map

我们将上面自动创建的东西全都删除,用Map来匹配路由,然后通过不同的代理处理请求。

    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            var app = builder.Build();
           
            //匹配map1 请求
            app.Map("/map1", new Action<IApplicationBuilder>((app) =>
            {
                app.Run(async context =>
                {
                    await context.Response.WriteAsync("map1 run");
                });
            }));
            //匹配map2 请求
            app.Map("/map2", new Action<IApplicationBuilder>((app) =>
            {
                app.Run(async context =>
                {
                    await context.Response.WriteAsync("map2 run");
                });
            }));

            app.Run();
        }
    }
  • 请求map1 我们输出:map1 run

image

  • 请求map2 我们输出:map2 run

image

Asp.Net Core MapControllers 的扩展方法也是类似道理,用来匹配路由调用处理程序。

四、Run

在上面的 Map 后面我们使用的处理方法中 Run 用于终止管道。也就是说在该管道中如果调用了 Run 那么就直接返回了,即使你后面还添加了 Use 也不会执行。

app.Run(async context =>
{
    await context.Response.WriteAsync("map1 run");
});

Map 相当于是迎客进门,Map 上了就用指定的管道进行处理,如果没有 Map 上就调用主管道,也就是主管道上的其他中间件也会执行处理。比如我们再加一个 Run 用于没匹配上路由也输出点信息。

image

加了context.Response.ContentType = "text/plain; charset=utf-8"; 不然中文会乱码。

image

因为 Run 是终结点,那这个管道中我还想加其他处理怎么办呢,这个时候就该轮到 Use 出场了。

五、Use

用 Use 将多个请求委托链接在一起。 next 参数表示管道中的下一个委托。 可通过不调用 next 参数使管道短路。

首先我们在外面添加两个 Use,不放到 Map 中,这样的话就只有未匹配到的路由会调用

    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            var app = builder.Build();

            

            app.Map("/map1", new Action<IApplicationBuilder>((app) =>
            {
                app.Run(async context =>
                {
                    await context.Response.WriteAsync("map1 run");
                });
            }));

            app.Map("/map2", new Action<IApplicationBuilder>((app) =>
            {
                app.Run(async context =>
                {
                    await context.Response.WriteAsync("map2 run");
                });
            }));
            //Use1
            app.Use(async (context, next) =>
            {
                context.Response.ContentType = "text/plain; charset=utf-8";

                await context.Response.WriteAsync("第 1 个Use   开始!\r\n", Encoding.UTF8);

                await next();

                await context.Response.WriteAsync("第 1 个Use   结束!\r\n", Encoding.UTF8);

            });
            
            //Use2
            app.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("第 2 个Use   开始!\r\n", Encoding.UTF8);

                await next();

                await context.Response.WriteAsync("第 2 个Use   结束!\r\n", Encoding.UTF8);

            });
            //结束管道处理
            app.Run(async context =>
            {
                await context.Response.WriteAsync("未匹配处理!\r\n", Encoding.UTF8);
            });

            app.Run();
        }
    }

最后执行的路径和最开始的图是一致的。

image

为什么将context.Response.ContentType = "text/plain; charset=utf-8"; 放到第一个 Use 呢,因为如果放到 Run 里面会报错,改变了 Header 标头。所以理论上也不要在 Use 里面发送响应WriteAsync,此处为了演示所以这么写。

image

六、中间件类

上面的代理方法可以移动到类中,这个类就是中间件类。中间件类需要如下要求:

  • 具有类型为 RequestDelegate 的参数的公共构造函数。
  • 名为 Invoke 或 InvokeAsync 的公共方法。 此方法必须:
    返回 Task。
    接受类型 HttpContext 的第一个参数。

构造函数和 Invoke/InvokeAsync 的其他参数由依赖关系注入 (DI) 填充。

将上面的未匹配路由处理逻辑移动到中间件类中:

  • TestMiddleware1:
    public class TestMiddleware1
    {
        private readonly RequestDelegate _next;

        public TestMiddleware1(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {

            context.Response.ContentType = "text/plain; charset=utf-8";

            await context.Response.WriteAsync("第 1 个Use   开始!\r\n", Encoding.UTF8);

            await _next(context);

            await context.Response.WriteAsync("第 1 个Use   结束!\r\n", Encoding.UTF8);
        }
    }
  • TestMiddleware2
    public class TestMiddleware2
    {
        private readonly RequestDelegate _next;

        public TestMiddleware2(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {


            await context.Response.WriteAsync("第 2 个Use   开始!\r\n", Encoding.UTF8);

            await _next(context);

            await context.Response.WriteAsync("第 2 个Use   结束!\r\n", Encoding.UTF8);
        }
    }
  • Program
    image

  • 运行
    image

此处的中间件使用有顺序问题,如果我先app.UseMiddleware<TestMiddleware2>() 因为 TestMiddleware1 修改了标头,根据约定是不允许的,所以程序是有报错。

image

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

七、中间件顺序

image

以上是内置中间件的默认顺序规则,具体如何使用内置中间件,可参阅官方资料。

八、写在最后

以上就是关于中间件的部分知识,结合我自己的理解做了前后衔接的梳理逻辑。

官方网站更多的是讲解每个知识点的细节,前后需要结合起来理解,当然我还是强烈建议跟着官方文档学习,而且是最权威最可信的:ASP.NET Core 中间件

这个系列历时2年,工作生活都比较忙,也有放纵啥事不相干的时候,中间断断续续的,总算是坚持完了。很多东西就是这样,累了就休息一下贵在坚持,即使再慢,积累的成果也有收获。

标签:WebAPI,Use,温故知新,app,await,中间件,context,Response
From: https://www.cnblogs.com/SunSpring/p/17969336

相关文章

  • net6 webapi swagger 中文\版本说明
    1、新建ApiVersionInfo.cs版本文件;///<summary>///API版本///</summary>publicclassApiVersionInfo{publicstaticstring接口版本1;}右键编辑项目文件;<PropertyGroup><GenerateDocumentationFile>true</GenerateDocumentationFile>......
  • net6 webapi cors 跨域
    1、nuget安装microsoft.aspnetcore.cors2、program.cs文件中usingSystem.Reflection;usingMicrosoft.OpenApi.Models;varbuilder=WebApplication.CreateBuilder(args);//设置跨域builder.Services.AddCors(options=>{options.AddPolicy("Cors",builder......
  • 前后端分离,Asp.net core webapi 简单 2 步,轻松配置跨域
    前言可以说,前后端分离已经成为当今信息系统项目开发的主流软件架构模式,微服务的出现,让前后端分离发展更是迅速,大量优秀的前端框架如vue.js、react的出现,也让前后端分离趋势加快。所谓的前后端分离软件架构模式,就是指将前端和后端的开发完全分离,后端负责提供API接口和数据处理......
  • C# 自定义日志中间件 ASP.NET Core Web API
    自定义日志中间件usingMicrosoft.AspNetCore.Builder;usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Http.Extensions;usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Logging;usingNewtonsoft.Json;usingSystem;usingS......
  • .net core 中什么是中间件
    在.NETCore中,中间件(Middleware)是ASP.NETCore应用程序处理请求和响应的组件。中间件位于应用程序的请求处理管道中,它可以截获请求,执行一些逻辑,并将请求传递给下一个中间件或终止请求的执行。中间件的主要作用是实现横切关注点,处理跨请求的功能和任务,例如身份验证、异常处理......
  • Gin中间件
    Gin中间件1中间件简介/* Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。*/在gin中的Default的方法中已经有两个中间件了://Defaul......
  • Django 源码(三)-应用 & 中间件 & 配置文件
    Django源码(三)-应用&中间件&配置文件本部分主要是在为程序启动时候加载应用以及中间件的信息;1.应用的加载在程序启动的部分,我们分析到程序执行的时候都会执行一个setup()函数,相关的内容可以看之前的章节的部分;defsetup(set_prefix=True):"""Configurethes......
  • C#调用webapi发送带json参数的post请求
    嗯。。很久不更新,因为跳槽新公司了,要学的东西太多太忙了。也没时间记录,今天又写了一个C#调用webapi发送带json参数的post请求拿数据的方法,所以来到这里记录一下///<paramname="url">请求地址</param>///<paramname="jsonParas">请求体</param>///<paramnam......
  • 中间件 ZK分布式专题与Dubbo微服务入门 4-8 权限acl详解,acl的构成-scheme与id
    0课程地址https://coding.imooc.com/lesson/201.html#mid=12704 1重点关注1.1权限的构成权限字符串缩写crdwaCREATE:创建子节点READ:获取节点/子节点WRITE:设置节点数据 DELETE:删除子节点ADMIN:设置权限  2课程内容  ......
  • 中间件 ZK分布式专题与Dubbo微服务入门 4-9 acl的构成-permissions
    0课程地址https://coding.imooc.com/lesson/201.html#mid=12705 1重点关注1.1权限的构成权限字符串缩写crdwaCREATE:创建子节点READ:获取节点/子节点WRITE:设置节点数据 DELETE:删除子节点ADMIN:设置权限  2课程内容  ......