返回顶部
首页 > 资讯 > 后端开发 > ASP.NET >ASP.NETCore中间件
  • 692
分享到

ASP.NETCore中间件

2024-04-02 19:04:59 692人浏览 独家记忆
摘要

目录1.前言2.使用中间件2.1 Run2.2 Use2.3 Map和MapWhen3.顺序4.编写中间件(重点)4.1中间件类4.2中间件扩展方法5.按每次请求创建依赖注入(DI)

1.前言

整个Http Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline)。而中间件(middleware)则是一种装配到请求管道以处理请求和响应的组件。每个组件:

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

中间件(middleware)处理流程如下图所示:

2.使用中间件

ASP.net core请求管道中每个中间件都包含一系列的请求委托(request delegates)来处理每个HTTP请求,依次调用。请求委托通过使用IApplicationBuilder类型的Run、Use和Map扩展方法在Strartup.Configure方法中配置。下面我们通过配置Run、Use和Map扩展方法示例来了解下中间件。

2.1 Run

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
    //第一个请求委托Run
    app.Run(async context =>//内嵌匿名方法
    {
        await context.Response.WriteAsync("Hello, World!");
    });
    //第二个请求委托Run
    app.Run(async context =>//内嵌匿名方法
    {
        await context.Response.WriteAsync("Hey, World!");
    });
    }
}

响应结果:

由上述代码可知,Run方法指定为一个内嵌匿名方法(称为并行中间件,in-line middleware),而内嵌匿名方法中并没有指定执行下一个请求委托,这一个过程叫管道短路,而该中间件又叫“终端中间件”(terminal middleware),因为它阻止中间件下一步处理请求。所以在Run第一个请求委托的时候就已经终止请求,并没有执行第二个请求委托直接返回Hello, World!输出文本。而根据官网解释,Run是一种约定,有些中间件组件可能会暴露他们自己的Run方法,而这些方法只能在管道末尾处运行(也就是说Run方法只在中间件执行最后一个请求委托时才使用)。

2.2 Use

public void Configure(IApplicationBuilder app)
{
    app.Use(async (context, next) =>
    {
        context.Response.ContentType = "text/plain; charset=utf-8";
        await context.Response.WriteAsync("进入第一个委托 执行下一个委托之前\r\n");
        //调用管道中的下一个委托
        await next.Invoke();
        await context.Response.WriteAsync("结束第一个委托 执行下一个委托之后\r\n");
    });
    app.Run(async context =>
    {
        await context.Response.WriteAsync("进入第二个委托\r\n");
        await context.Response.WriteAsync("Hello from 2nd delegate.\r\n");
        await context.Response.WriteAsync("结束第二个委托\r\n");
    });
}

响应结果:

由上述代码可知,Use方法将多个请求委托链接在一起。而next参数表示管道中的下一个委托。如果不调用next参数调用下一个请求委托则会使管道短路。比如,一个授权(authorization)中间件只有通过身份验证之后才能调用下一个委托,否则它就会被短路,并返回“Not Authorized”的响应。所以应尽早在管道中调用异常处理委托,这样它们就能捕获在管道的后期阶段发生的异常。

2.3 Map和MapWhen

  • Map:Map扩展基于请求路径创建管道分支。
  • MapWhen:MapWhen扩展基于请求条件创建管道分支。

Map示例:

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);
        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

下面表格使用前面的代码显示来自http://localhost:5001的请求和响应。

请求

响应

localhost:5001

Hello from non-Map delegate.

localhost:5001/map1

Map Test 1

localhost:5001/map2

Map Test 2

localhost:5001/map3

Hello from non-Map delegate.

由上述代码可知,Map方法将从HttpRequest.Path中删除匹配的路径段,并针对每个请求将该路径追加到HttpRequest.PatHBase。也就是说当我们在浏览器上输入map1请求地址的时候,系统会执行map1分支管道输出其请求委托信息,同理执行map2就会输出对应请求委托信息。
MapWhen示例:

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }
    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

下面表格使用前面的代码显示来自http://localhost:5001的请求和响应。

请求

响应

http://localhost:5001

Hello from non-Map delegate. <p>

https://localhost:5001/?branch=master

Branch used = master

由上述代码可知,MapWhen是基于branch条件而创建管道分支的,我们在branch条件上输入master就会创建其对应管道分支。也就是说,branch条件上输入任何一个字符串条件,都会创建一个新的管理分支。
而且还Map支持嵌套,例如:

public void Configure(IApplicationBuilder app)
{
    app.Map("/level1", level1App => {
        level1App.Map("/level2a", level2AApp => {
            // "/level1/level2a" processing
        });
        level1App.Map("/level2b", level2BApp => {
            // "/level1/level2b" processing
        });
    });
}

还可同时匹配多个段:

public class Startup
{
    private static void HandleMultiSeg(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map multiple segments.");
        });
    }
    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1/seg1", HandleMultiSeg);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

3.顺序

向Startup.Configure方法添加中间件组件的顺序定义了在请求上调用它们的顺序,以及响应的相反顺序。此排序对于安全性、性能和功能至关重要。
以下Startup.Configure方法将为常见应用方案添加中间件组件:

  • 异常/错误处理(Exception/error handling)
  • HTTP严格传输安全协议(HTTP Strict Transport Security Protocol)
  • HTTPS重定向(HTTPS redirection)
  • 静态文件服务器(Static file server)
  • Cookie策略实施(Cookie policy enforcement)
  • 身份验证(Authentication)
  • 会话(Session)
  • mvc

请看如下代码:

public void Configure(IApplicationBuilder app)
{
    if (env.IsDevelopment())
    {
        // When the app runs in the Development environment:
        //   Use the Developer Exception Page to report app runtime errors.
        //   Use the Database Error Page to report database runtime errors.
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        // When the app doesn't run in the Development environment:
        //   Enable the Exception Handler Middleware to catch exceptions
        //     thrown in the following middlewares.
        //   Use the HTTP Strict Transport Security Protocol (HSTS)
        //     Middleware.
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }
    // Return static files and end the pipeline.
    app.UseStaticFiles();
    // Authenticate before the user accesses secure resources.
    app.UseAuthentication();
}

从上述示例代码中,每个中间件扩展方法都通过Microsoft.Aspnetcore.Builder命名空间在 IApplicationBuilder上公开。但是为什么我们要按照这个顺序去添加中间件组件呢?下面我们挑几个中间件来了解下。

  • UseExceptionHandler(异常/错误处理)是添加到管道的第一个中间件组件。因此我们可以捕获在应用程序调用中发生的任何异常。那为什么要将异常/错误处理放在第一位呢?那是因为这样我们就不用担心因前面中间件短路而导致捕获不到整个应用程序所有异常信息。
  • UseStaticFiles(静态文件)中间件在管道中提前调用,方便它可以处理请求和短路,而无需通过剩余中间组件。也就是说静态文件中间件不用经过UseAuthentication(身份验证)检查就可以直接访问,即可公开访问由静态文件中间件服务的任何文件,包括wwwroot下的文件。
  • UseAuthentication(身份验证)仅在MVC选择特定的Razor页面或Controller和Action之后才会发生。

经过上面描述,大家都了解中间件顺序的重要性了吧。

4.编写中间件(重点)

虽然ASP.net core为我们提供了一组丰富的内置中间件组件,但在某些情况下,你可能需要写入自定义中间件。

4.1中间件类

下面我们自定义一个查询当前区域性的中间件:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use((context, next) =>
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);
                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;
            }
            // Call the next delegate/middleware in the pipeline
            return next();
        });
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}

可通过传入区域性参数条件进行测试。例如http://localhost:7997/?culture=zh、http://localhost:7997/?culture=en。
但是如果每个自定义中间件都在Startup.Configure方法中编写如上一大堆代码,那么对于程序来说,将是灾难性的(不利于维护和调用)。为了更好管理代码,我们应该把内嵌匿名方法封装到新建的自定义类(示例自定义RequestCultureMiddleware类)里面去:

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;
    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task InvokeAsync(HttpContext context)
    {
        context.Response.ContentType = "text/plain; charset=utf-8";
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }
        // Call the next delegate/middleware in the pipeline
        await _next(context);
    }
}

通过Startup.Configure方法调用中间件:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<RequestCultureMiddleware>();
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}

4.2中间件扩展方法

Startup.Configure方法调用中间件设置可以通过自定义的扩展方法将其公开(调用内置IApplicationBuilder公开中间件)。示例创建一个RequestCultureMiddlewareExtensions扩展类并通过IApplicationBuilder公开:

public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

再通过Startup.Configure方法调用中间件:

public class Startup
{
    public void Configure(IApplicationBuilder app)
{
        app.UseRequestCulture();
        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}

响应结果:

通过委托构造中间件,应用程序在运行时创建这个中间件,并将它添加到管道中。这里需要注意的是,中间件的创建是单例的,每个中间件在应用程序生命周期内只有一个实例。那么问题来了,如果我们业务逻辑需要多个实例时,该如何操作呢?请继续往下看。

5.按每次请求创建依赖注入(DI)

在中间件的创建过程中,内置的ioc容器会为我们创建一个中间件实例,并且整个应用程序生命周期中只会创建一个该中间件的实例。通常我们的程序不允许这样的注入逻辑。其实,我们可以把中间件理解成业务逻辑的入口,真正的业务逻辑是通过Application Service层实现的,我们只需要把应用服务注入到Invoke方法中即可。ASP.Net Core为我们提供了这种机制,允许我们按照请求进行依赖的注入,也就是每次请求创建一个服务。示例:

public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    public CustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    // IMyScopedService is injected into Invoke
    public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
    {
        svc.MyProperty(1000);
        await _next(httpContext);
    }
}
public static class CustomMiddlewareExtensions
{
    public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CustomMiddleware>();
    }
}
public interface IMyScopedService
{
    void MyProperty(decimal input);
}
public class MyScopedService : IMyScopedService
{
    public void MyProperty(decimal input)
    {
        Console.WriteLine("MyProperty is " + input);
    }
}
public void ConfigureServices(IServiceCollection services)
{
    //注入DI服务
    services.AddScoped<IMyScopedService, MyScopedService>();
}

响应结果:

到此这篇关于asp.net Core中间件的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: ASP.NETCore中间件

本文链接: https://lsjlt.com/news/145114.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • ASP.NETCore中间件
    目录1.前言2.使用中间件2.1 Run2.2 Use2.3 Map和MapWhen3.顺序4.编写中间件(重点)4.1中间件类4.2中间件扩展方法5.按每次请求创建依赖注入(DI)...
    99+
    2024-04-02
  • ASP.NETCore基础之中间件
    什么是ASP.NET Core Middleware? ASP.NET Core中间件组件是被组装到应用程序管道中以处理HTTP请求和响应的软件组件(从技术上来说,组件只是C#类)。...
    99+
    2024-04-02
  • 详解ASP.NETCore中间件Middleware
    本文为官方文档译文,官方文档现已非机器翻译 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/...
    99+
    2024-04-02
  • ASP.NETCore基础之异常中间件
    了解异常中间件 首先,使用ASP.NET模板创建一个核心应用程序。默认情况下,ASP.NET核心应用程序只是返回应用程序未处理的异常的状态代码。如下所示,我们引发异常。 运行应用程...
    99+
    2024-04-02
  • ASP.NETCore实现中间件的几种方式
    前言 ASP.NET Core 中 HTTP 管道使用中间件组合处理的方式, 换句人话来说, 对于写代码的人而言,一切皆中间件. 业务逻辑/数据访问/等等一切都需要以中间件的方式来呈...
    99+
    2024-04-02
  • ASP.NETCore模仿中间件方式实现列表过滤功能
    我们的很多功能当中都会遇到对版本进行过滤的场合,例如你可能需要对列表中的数据的时间进行过滤、版本过滤、渠道以及地区信息进行过滤。 原本的做法:设计很多个过滤方法,通过枚举的方式组合,...
    99+
    2024-04-02
  • ASP.NETCore中间件会话状态读写及生命周期示例
    目录前言:1) 关于Http中的会话2) 关于 ASP.NET Core 中的会话一、配置会话中间件二、会话状态的读写三、 示例的生命周期四、其他前言: 本文使用 .NET Core...
    99+
    2024-04-02
  • ASP.NETCore中的静态文件介绍
    静态文件(HTML,CSS,图片和Javascript之类的资源)会被ASP.NET Core应用直接提供给客户端。 静态文件通常位于网站根目录(web root) <cont...
    99+
    2024-04-02
  • ASP.NETCore读取配置文件
    ASP.NET Core 中,可以使用 ConfigurationBuilder 对象来构建。 主要分为三部:配置数据源 -> ConfigurationBuilder -&g...
    99+
    2024-04-02
  • ASP.NETCore中使用Swagger
    一、什么是Swagger 随着技术的不断方法,现在的网站开发基本都是使用前后端分离的模式,这样使前端开发者和后端开发者只需要专注自己擅长的即可。但这种方式会存在一种问题:前后端通过A...
    99+
    2024-04-02
  • ASP.NETCore中的Razor页面使用视图组件
    视图组件简介 在新的ASP.NET Core MVC中,视图组件类似于局部视图,但它们更强大。视图组件不使用模型绑定,仅依赖于您在调用时提供的数据。 视图组件特性: 呈现页面响应的某...
    99+
    2024-04-02
  • ASP.NETCore实现多文件上传
    创建应用程序 打开VS 2017   --新建 ASP.NET Core Web 应用程序     --Web 应用程序(模型视图控制器) 程序名字、路径,默认即可 删除不必要的内...
    99+
    2024-04-02
  • ASP.NETCore中的环境配置
    1.环境变量配置 ASP.NET Core在应用程序启动时读取环境变量(Properties\launchSettings.json)ASPNETCORE_ENVIRONMENT,并...
    99+
    2024-04-02
  • ASP.NETCore中使用多环境
    ASP.NET Core 支持在多个环境中管理应用程序,如开发(Development),预演(Staging)和生产(Production)。环境变量用来指示应用程序正在运行的环境...
    99+
    2024-04-02
  • ASP.NETCore项目中集成TypeScript
    今天试了下在Asp.net core中集成typescript,发现vs2019对typescript集成的支持还是非常友好的。本文在这里简单的介绍一下。 由于typescript文...
    99+
    2024-04-02
  • ASP.NETCore应用程序配置文件AppSetting.json
    AppSetting.json 当我们使用空项目模板或Razor页面或MVC模板或Web API模板创建ASP.NET Core Web应用程序时,Visual Studio会自动为...
    99+
    2024-04-02
  • ASP.NETCore实现文件上传和下载
    本文实例为大家分享了ASP.NET Core实现文件上传和下载的具体代码,供大家参考,具体内容如下 一、文件上传 1.1 获取文件后缀 /// <summary> ///...
    99+
    2024-04-02
  • ASP.NETCore中的Options选项模式
    1.前言 选项(Options)模式是对配置(Configuration)的功能的延伸。在12章(ASP.NET Core中的配置二)Configuration中有介绍过该功能(绑定...
    99+
    2024-04-02
  • 在Asp.netcore中实现websocket通信
    今天小试了一下在Asp.net core中使用websocket,这里记录一下: 在 Startup 类的 Configure 方法中添加 WebSocket 中间件。 app.Us...
    99+
    2024-04-02
  • ASP.NETCore中的对象池介绍
    asp.net core中通过扩展库的方式提供给了一个标准的对象池ObjectPool,定义在Microsoft.Extensions.ObjectPool.dll 程序集中。它本身...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作