博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【对比学习】koa.js、Gin与asp.net core——中间件
阅读量:4036 次
发布时间:2019-05-24

本文共 9764 字,大约阅读时间需要 32 分钟。

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 mainimport( "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 mainimport( "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 创建中间件管道

//Runpublic class Startup{ public void Configure(IApplicationBuilder app) {     app.Run(async context =>     {         await context.Response.WriteAsync("Hello, World!");     }); }}//Use - Runpublic 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. 

");        });    }    //请求会匹配 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

 类型的任何谓词均可用于将请求映射到管道的新分支。 在以下示例中,谓词用于检测查询字符串变量 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. 

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

 _logger;    public Startup(ILogger
 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
();        }    }}//调用中间件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 ,重写方法,如启用属性路由,创建自定义授权过滤器,创建自定义身份验证过滤器,模型验证过滤器

转载地址:http://kwudi.baihongyu.com/

你可能感兴趣的文章
JMeter 保持sessionId
查看>>
IDEA Properties中文unicode转码问题
查看>>
Idea下安装Lombok插件
查看>>
zookeeper
查看>>
Idea导入的工程看不到src等代码
查看>>
技术栈
查看>>
Jenkins中shell-script执行报错sh: line 2: npm: command not found
查看>>
8.X版本的node打包时,gulp命令报错 require.extensions.hasownproperty
查看>>
Jenkins 启动命令
查看>>
Maven项目版本继承 – 我必须指定父版本?
查看>>
Maven跳过单元测试的两种方式
查看>>
通过C++反射实现C++与任意脚本(lua、js等)的交互(二)
查看>>
利用清华镜像站解决pip超时问题
查看>>
[leetcode BY python]1两数之和
查看>>
微信小程序开发全线记录
查看>>
Centos import torchvision 出现 No module named ‘_lzma‘
查看>>
PTA:一元多项式的加乘运算
查看>>
CCF 分蛋糕
查看>>
解决python2.7中UnicodeEncodeError
查看>>
小谈python 输出
查看>>