【对比学习】koa.js、Gin与asp.net core——中间件

web框架中间件对比

编程语言都有所不同,各个语言解决同一类问题而设计的框架,确有共通之处,毕竟是解决同一类问题,面临的挑战大致相同,比如身份验证,api授权等等,鄙人对node.js,golang,.net core有所涉猎,对各自的web框架进行学习的过程中发现了确实有相似之处。下面即对node.js的koa、golang的gin与.net core的asp.net core三种不同的web后端框架的中间件做一个分析对比

Node-Koa.js

应用级中间件
//如果不写next,就不会向下匹配--匹配任何一个路由
app.use(async(ctx,next)=>{
    console.log(new Date())
    await next();
})
路由级中间件
 router.get('/news',async(ctx,next)=>{
     console.log("this is news")
     await next();
 })
错误处理中间件
app.use(async(ctx,next)=>{
    //应用级中间件 都需要执行
    /*
        1.执行若干代码
    */
    next();//2.执行next() 匹配其他路由

    //4.再执行
    if(ctx.status==404){
        ctx.status=404
        ctx.body="这是一个404"
    }else{
        console.log(ctx.url)
    }
})

//3.匹配下面的路由
 router.get('/news',async(ctx)=>{
     console.log("this is news")
     ctx.body="这是一个新闻页面"
 })
第三方中间件

静态资源中间件为例:静态资源地址没有路由匹配,盲目引入静态资源,会报404.

//安装
npm install koa-static --save

//使用
//引入
const static=require('koa-static')
//使用
app.use(static('static')) //去static文件目录中将中找文件,如果能找到对应的文件,找不到就next()

app.use(static(__dirname+'/static'))

app.use(static(__dirname+'/public'))
中间件执行顺序

洋葱执行:从上到下依次执行,匹配路由响应,再返回至中间件进行执行中间件,【先从外向内,然后再从内向外】

Golang-Gin

钩子(Hook)函数,中间件函数

定义中间件
package main

import(
  "github.com/gin-gonic/gin"
)

func main(){
    r:=gin.Default()
    r.GET("/index",func(c *gin.Context){
        //...
    })
    r.Run()
}

func m1(c *gin.Context){
    fmt.Println("中间件m1")

    c.Next()//调用后续的处理函数
    //c.Abort()//阻止调用后续的处理函数

    fmt.Println("m1 out...")
}

注册中间件
全局注册-某个路由单独注册-路由组注册
package main

import(
    "github.com/gin-gonic/gin"
)

func main(){
    r:=gin.Default()
    r.GET("/index",func(c *gin.Context){
        //...
    })

    //某个路由单独注册--也可以取名为路由级注册中间件
    r.GET("/test1",m1,func(c *gin.Context){
        //...
    })

    //路由组注册
    xxGroup:=r.Group("/xx",m1)
    {
        xxGroup.GET("/index",func(c *gin.Context){
            //...
        })
    }

    xx2Group:=r.Group("/xx2")
    xx2Group.Use(m1)
    {
        xxGroup.GET("/index",func(c *gin.Context){
            //...
        })
    }
    r.Run()
     r.GET("/index",m1)
}

func m1(c *gin.Context){
    fmt.Println("中间件m1")

    c.Next()//调用后续的处理函数
    //c.Abort()//阻止调用后续的处理函数
    //return 连下方的fmt.Println都不执行了,立即返回
    fmt.Println("m1 out...")
}

r.Use(m1)//全局注册

//多个中间件注册
r.Use(m1,m2)
中间件执行顺序

与koa中间件执行顺序一致

中间件通常写法-闭包
func authMiddleware(doCheck bool) gin.HandlerFunc{
    //连接数据库
    //或准备工作
    return func(c *gin.Context){
        //是否登录判断
        //if是登录用户
        //c.Next()
        //else
        //c.Abort()
    }
}
中间件通信
func m1(c *gin.Context){
    fmt.Println("m1 in ...")

    start := time.Now()
    c.Next()
    cost:=time.Since(start)
    fmt.Printf("cost:%v\n",cost)
    fmt.Println("m1 out...")
}

func m2(c *gin.Context){
    fmt.Println("m2 in...")
    //中间件存值
    c.Set("name","carfield")
    fmt.Println("m2 out...")
    //其他中间件取值
    // c.Get
    // c.MustGet
}

中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context) 必须使用其只读副本c.Copy(),否则会出现线程安全问题。

.Net Core-Asp.net core

创建中间件管道

使用IApplicationBuilder 创建中间件管道

//Run
public class Startup
{
 public void Configure(IApplicationBuilder app)
 {
     app.Run(async context =>
     {
         await context.Response.WriteAsync("Hello, World!");
     });
 }
}

//Use - Run
public class Startup
{
 public void Configure(IApplicationBuilder app)
 {
     app.Use(async (context, next) =>
     {
         // Do work that doesn't write to the Response.
         await next.Invoke();
         // Do logging or other work that doesn't write to the Response.
     });

     app.Run(async context =>
     {
         await context.Response.WriteAsync("Hello from 2nd delegate.");
     });
 }
}

//这个Use是不是跟koa的应用级中间件很像
创建中间件管道分支

Map 扩展用作约定来创建管道分支。 Map 基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支。koa和gin中路由匹配就是map这种,当不使用内置的mvc模板路由,我姑且称它为自定义路由

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>");
        });
    }
    //请求会匹配 map1...map2...没匹配到路由的统统会执行app.Run
}

//像golang的gin一样,map也支持嵌套
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.");
        });
    }
}

//MapWhen 基于给定谓词的结果创建请求管道分支。 Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 在以下示例中,谓词用于检测查询字符串变量 branch 是否存在:

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>");
        });
    }
}
//UseWhen 也是基于给定谓词的结果创建请求管道分支。 与 MapWhen 不同的是,如果这个分支发生短路或包含终端中间件,则会重新加入主管道:
public class Startup
{
    private readonly ILogger<Startup> _logger;

    public Startup(ILogger<Startup> logger)
    {
        _logger = logger;
    }

    private void HandleBranchAndRejoin(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            var branchVer = context.Request.Query["branch"];
            _logger.LogInformation("Branch used = {branchVer}", branchVer);

            // Do work that doesn't write to the Response.
            await next();
            // Do other work that doesn't write to the Response.
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranchAndRejoin);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from main pipeline.");
        });
    }
}
内置中间件
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        //开发人员异常页中间件 报告应用运行时错误
        app.UseDeveloperExceptionPage();

        //数据库错误页中间件报告数据库运行时错误
        app.UseDatabaseErrorPage();
    }
    else
    {
        //异常处理程序中间件
        app.UseExceptionHandler("/Error");
        //http严格传输安全协议中间件
        app.UseHsts();
    }

    //HTTPS重定向中间件
    app.UseHttpsRedirection();

    //静态文件中间件
    app.UseStaticFiles();

    //Cookie策略中间件
    app.UseCookiePolicy();

    //路由中间件
    app.UseRouting();

    //身份验证中间件
    app.UseAuthentication();

    //授权中间件
    app.UseAuthorization();

    //会话中间件-如果使用session,就需要把cookie策略中间件先使用了,再引入session中间件,再引入mvc中间件,毕竟session是依赖cookie实现的
    app.UseSession();

    //终结点路由中间件
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
自定义中间件
Configure中直接写
//在Startup.Configure直接编码
public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            //做一些操作

            // Call the next delegate/middleware in the pipeline
            await next();
        });

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello world");
        });
    }
中间件类+中间件扩展方法+UseXX

Startup.Configure直接编码,当定义多个中间件,代码难免变得臃肿,不利于维护,看看内置的中间件,app.UseAuthentication();多简洁,查看asp.net core源码,内置的中间件都是一个中间件类xxMiddleware.cs 一个扩展方法 xxMiddlewareExtensions.cs 然后在Startup.Configure 中使用扩展方法调用Usexx()

using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;

namespace Culture
{
    public class RequestTestMiddleware
    {
        private readonly RequestDelegate _next;

        //具有类型为 RequestDelegate 的参数的公共构造函数
        public RequestTestMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        //名为 Invoke 或 InvokeAsync 的公共方法。 此方法必须:
        //返回 Task。
        //接受类型 HttpContext 的第一个参数。
        public async Task InvokeAsync(HttpContext context)
        {
            //做一些操作

            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }
    }
}

//中间件扩展方法
using Microsoft.AspNetCore.Builder;

namespace Culture
{
    public static class RequestTestMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestTest(
            this IApplicationBuilder app)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            return app.UseMiddleware<RequestTestMiddleware>();
        }
    }
}

//调用中间件
public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRequestTest();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}

.Net -Asp.Net

对于asp.net core的中间件与koa.js,gin中间件,实现形式略有不同,但是终极目标只有一个,就是AOP,面向切面编程,减少代码量,不至于在某一个路由匹配的方法中去编写同样的代码。在asp.net core之前,还是asp.net的时候,也有类似的AOP实现,去继承各种FilterAttribute ,重写方法,如启用属性路由,创建自定义授权过滤器,创建自定义身份验证过滤器,模型验证过滤器

原文地址:https://www.cnblogs.com/RandyField/p/12258189.html

时间: 2024-10-02 04:58:41

【对比学习】koa.js、Gin与asp.net core——中间件的相关文章

ASP.NETCore学习记录(二) —— ASP.NET Core 中间件

ASP.NET Core 中间件 目录: IApplicationBuilder 什么是中间件 ? 使用 IApplicationBuilder 创建中间件 Run.Map 与 Use 方法 实战中间件 参考原文 我们知道在 ASP.NET 中,有一个面向切面的请求管道,由22个主要的事件构成,能够让我们在往预定的执行顺序里面添加自己的处理逻辑.一般采取两种方式:一种是直接在 Global.asax 中对应的方法中直接添加代码.一种是是在 web.config 中通过注册 HttpModule

ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析

ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因为WCF中不仅仅只是有SOAP, 它还包含很多如消息安全性,生成WSDL,双工信道,非HTTP传输等. ASP.NET Core 官方推荐大家使用RESTful Web API的解决方案提供网络服务. SOAP 即 Simple Object AccessProtocol 也就是简单对象访问协议.

12.ASP.NET Core 中间件组件

这篇文章中,我将带领大家一起详细学习:ASP.NET Core Middleware Components.这篇文章中,我将详细讨论下面几个问题: 什么是ASP.NET Core 中的中间件组件? ASP.NET Core应用程序中,在哪里来使用中间件组件? 怎样来配置ASP.NET Core 应用程序中的中间件组件? 使用中间件组件的例子有哪些? ASP.NET Core应用程序中,中间件组件执行的顺序是? 什么是ASP.NET Core中间件组件? ASP.NET Core中间件组件就是组装

Asp.net core 中间件简单应用

Asp.net core中间件 ,处理http请求和响应的中间组件,对比起asp.net ,asp.net core 管道机制,可以说是帅气十足,简单直接.下面是通过中间件对一个请求的url 指定路由 新建webapi 项目 Startup类中Configure方法中添加处理中间件代码如下 public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app

ASP.NET Core 中间件基本用法

ASP.NET Core 中间件 ASP.NET Core的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件.中间件按照装配的先后顺序执行,并决定是否进入下一个组件.中间件管道的处理流程如下图(图片来源于官网): 管道式的处理方式,更加方便我们对程序进行扩展. 使用中间件 ASP.NET Core中间件模型是我们能够快捷的开发自己的中间件,完成对应用的扩展,我们先从一个简单的例子了解一下中间件的开发. Run 首先,我们创建一个ASP.NET Core 应用,在Startup

ASP.NET Core - 中间件与管道(1)

今天来讨论一个ASP.NET Core 很重要概念管道和中间件,在ASP.NET Core中,针对HTTP请求采用pipeline也就是通常说的管道方式来处理,而管道容器内可以挂载很多中间件(处理逻辑)“串联”来处理HTTP请求,每一个中间件都有权决定是否需要执行下一个中间件,或者直接做出响应.这样的机制使得HTTP请求能够很好的被层层处理和控制,并且层次清晰处理起来甚是方便. 示意图如下: 为了再次说明管道和中间件的概念,举一个官方给出的权限验证的例子,中间件A,B分别按顺序挂载在管道容器中,

ASP.NET Core 中间件详解及项目实战

前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章对你有用的话,不妨点个[推荐]. 目录 中间件(Middleware)的作用 中间件的运行方式 中间件(Middleware)和过滤器(Filter)的区别 什么情况我们需要中间件 怎么样自定义自己的中间件 中间件(Middleware)的作用 我们知道,任何的一个web框架都是把http请求封装成

asp.net core 中间件粗解

中间件 中间件在asp.net core中非常重要,它用来处理httpcontext.而httpcontext封装了请求和响应.也就是说,中间件是用来处理请求和响应的. 本质上,中间件被封装到了IApplicationBuilder这个接口中,他的实现类是ApplicationBuilder.源码在github:https://github.com/aspnet/HttpAbstractions ApplicationBuilder有两个方法和一个字段比较重要: private readonly

ASP.NET Core中间件计算Http请求时间

ASP.NET Core通过RequestDelegate这个委托类型来定义中间件 public delegate Task RequestDelegate(HttpContext context); 可将一个单独的请求委托并行指定为匿名方法(称为并行中间件),或在类中对其进行定义.可通过Use,或在Middleware类中配置要传递给委托执行的方法(参数类型HttpContext,返回值类型Task). public static IApplicationBuilder Use(this IA