【对比学习】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=404ctx.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 创建中间件管道

//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 pipelineawait 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 pipelineawait _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 ,重写方法,如启用属性路由,创建自定义授权过滤器,创建自定义身份验证过滤器,模型验证过滤器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/306211.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 传送解析8583报文_java发送ISO8583报文接口案例

【实例简介】java发送ISO8583报文实现平安银行支付接口【实例截图】【核心代码】pingAnBank└── pingAnBank├── build.xml├── client│ ├── ClientBean.java│ ├── UDPClient.java│ └── UdpClientSocket.java├── com│ └── solab│ └── i…

2020年终回顾:时间会回答成长,成长会回答梦想

前言2020年是脚踏实地&#xff0c;慢慢成长的一年&#xff0c;由于疫情的缘故&#xff0c;今年社区没有像去年一样举办多场线下活动&#xff0c;不过 .NET CONF CHINA 大会昨天也在苏州顺利召开&#xff0c;回顾这一年&#xff0c;也有不少惊喜与感悟2020年回顾公众号自从去年双…

java servlet 跳转_Servlet跳转方式sendReDirect()和forward()

在web应用服务中&#xff0c;经常会面对不同SERVLET之间的跳转&#xff0c;目前我们可以通过以下两种方式实现&#xff1a;1.RequestDispatcher.forward()2.ServletResponse.sendReDirect()两者的区别&#xff1a;1.redirect 方式可以跨应用访问,forward 只能在同一个应用中跳转…

明天面腾讯,我刷了这71道面试题...

激动人心的Conf 2020中国.NET开发者大会完美落幕&#xff0c;有幸去到现场&#xff0c;跟诸位.NET大佬、微软大咖、MVP面对面交流&#xff0c;内心很是鸡冻&#xff01;聊天中我注意到一个细节&#xff0c;很多公司的项目都在逐步用MySQL替换SQLServer&#xff0c;尤其是微服务…

java 毕向东 内部类_java基础内部类(毕向东老师)

内部类//特点&#xff1a;内部类可以直接访问外部类的成员&#xff0c;//外部类要访问内部类中的成员必须创建内部类的对象。//为什么要定义内部类呢&#xff1f;类是用于描述事物的&#xff0c;而事务中如果还有具体的事物&#xff0c;而且这个内部的事物在访问着所属事物中的…

java字符串切分_Java字符串分割(转)

java.lang.String的split()方法, JDK 1.4 or laterpublic String[] split(String regex,int limit)示例代码public class StringSplit {public static void main(String[] args) {String sourceStr "1,2,3,4,5";String[] sourceStrArray sourceStr.split(",&q…

面试官:. NET5源码里用到了哪些设计模式?懵!

作为微软最早迈向开源的重要软件之一&#xff0c;.NET 5的发布具有重要意义&#xff01;微软希望 .NET Framework 开发者能够迁移他们的代码和应用到 .NET 5.0 上&#xff0c;为明年发布的 .NET 6.0 将 Xamarin 开发者过渡到统一平台奠定基础。版本发布时间轴&#xff1a;.NET …

ldap java 密码_用java进行LDAP用户登陆(用户认证)及修改密码

一、用户认证public String execute(){Hashtable env new Hashtable();String LDAP_URL "ldap://8.8.8.8:389"; // LDAP访问地址env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");env.put(Context.PROVIDER_URL, LDAP_URL)…

C# 中 ConcurrentDictionary 一定线程安全吗?

根据 .NET 官方文档的定义&#xff1a;ConcurrentDictionary<TKey,TValue> Class 表示可由多个线程同时访问的线程安全的键/值对集合。这也是我们在并发任务中比较常用的一个类型&#xff0c;但它真的是绝对线程安全的吗&#xff1f;仔细阅读官方文档&#xff0c;我们会发…

java 3des 32位密钥_3des,java_java 中32位秘钥长度的 3des加密方法?,3des,java - phpStudy...

java 中32位秘钥长度的 3des加密方法&#xff1f;java中 3des加密 默认是24位秘钥的现在需求是 32位秘钥加密尝试很多种方法 解决 结果都不正确// 密钥private final static String secretKey "11111111111111111111111111111111";// 向量// private final static S…

2020 .NET 开发者峰会顺利在苏州落幕,相关数据很喜人以及线上直播回看汇总

在2019年上海中国.NET开发者大会的基础上&#xff0c;2020年12月19-20日 继续以“开源、共享、创新” 为主题的第二届中国 .NET 开发者峰会&#xff08;.NET Conf China 2020&#xff09;在苏州人工智能智能产业创新中心落下帷幕&#xff0c;本次大会以线下城市苏州为中心&…

java怎么判断数据类型_数据类型判断

[java]代码库import java.util.*; public class Main{public static void main(String[] args) {Scanner scan = new Scanner(System.in); String s = scan.nextLine(); String []str = s.split(" "); int i, j; for(i = 0; i < str.length; i++) {if(i != 0) Sys…

.NET 云原生架构师训练营(模块二 基础巩固 REST RESTful)--学习笔记

2.3.1 Web API -- REST && RESTful什么是 REST&#xff0c;什么是 RESTfulRESTful API 设计RESTful 成熟度模型什么是 REST&#xff0c;什么是 RESTful理解RESTful架构&#xff1a;https://www.ruanyifeng.com/blog/2011/09/restful.htmlREST&#xff08;Representatio…

vue 一个组件内多个弹窗_论如何用Vue实现一个弹窗-一个简单的组件实现

前言最近在使用element-ui框架&#xff0c;用到了Dialog对话框组件&#xff0c;大致实现的效果&#xff0c;跟我之前自己在移动端项目里面弄的一个弹窗组件差不太多。然后就想着把这种弹窗组件的实现方式与大家分享一下&#xff0c;下面本文会带着大家手摸手实现一个弹窗组件。…

拆分路径 java_JAVA 类文件中的路径如何拆分和替换

我做Swing的时候文件要放绝对路径&#xff0c;相对路径出不来&#xff01;所以我用如果我的类放在D:\aaa\Class里Thread.currentThread().getContextClassLoader().getResource("");MenuTest.class.getClas...我做Swing的时候文件要放绝对路径&#xff0c;相对路径出…

为 CefSharp 应用内置 C++ 运行环境并启用 AnyCPU 支持

一个 CefSharp 应用程序要想正确运行&#xff0c;有两个必要条件&#xff1a;.NET Framework 4.5.2VC 2015在部署 CefSharp 应用时经常会遇到因为没有 VC 2015 而无法运行的问题&#xff1a;通过事件查看器&#xff0c;可以观察到一个类型为&#xff1a;System.IO.FileNotFound…

java file rename 失败_java重命名文件造成文件不可读写

我想使用java代码对nginx日志文件进行拆分&#xff0c;但是我发现代码执行之后&#xff0c;拆分出来的日志文件没有读写权限&#xff0c;查看文件属性&#xff0c;显示的很诡异&#xff1a;点击高级按钮&#xff0c;显示你没有权限查看或者编辑这个对象的权限设置&#xff1a;反…

如何在 ASP.NET Core 中使用 Route 特性

ASP.NET Core 中的 Route 中间件的职责在于将 request 匹配到各自 Route 处理程序上&#xff0c;Route 分两种&#xff1a;基于约定 和 基本特性 模式。基于约定 模式的Route采用集中化的方式&#xff0c;而 基于特性 的方式允许你在 Action 或者 Controller 上单独定义&#x…

java opencsv_用opencsv文件读写CSV文件

首先明白csv文件长啥样儿&#xff1a;用excel打开就变成表格了&#xff0c;看不到细节推荐用其它简单粗暴一点儿的编辑器&#xff0c;比如Notepad&#xff0c;csv文件内容如下&#xff1a;csv文件默认用逗号分隔各列。有了基础的了解就进入主题&#xff0c;用Opencsv读写csv文件…

Beetlex之tcp/tls服务压测工具

在编写tcp服务的时候经常需要对服务的基础性能进行一个压力测试&#xff0c;虽然网上这些工具有很多&#xff0c;但具备使用方便和高强度的测试工具则不多。为了方便这方面的高强度压测所以在beetlex的基础扩展这样一个工具。安装可以访问https://github.com/beetlex-io/TCPBen…