【比較学習】koa.js、Ginとasp.Netcore——ミドルウェア

11955 ワード

Webフレームワークミドルウェアの比較


プログラミング言語はすべて異なっていて、それぞれの言語は同じ種類の問題を解決して設計した枠組み、確かに共通点があって、結局同じ種類の問題を解決して、直面する挑戦は大体同じで、例えば認証、apiの授権など、私はnodeに対して.js,golang,.Netcoreには関与があり,それぞれのwebフレームワークを学習する過程で確かに類似点が見つかった.次はnode.jsのkoa、golangのginと.Netcoreのasp.Netcoreの3つの異なる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


フック関数
ミドルウェアの定義
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
",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. 

"); }); } // 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");
        });
    }

ミドルウェアクラス+ミドルウェア拡張方法+UseXXStartup.Configureで直接符号化すると、複数のミドルウェアを定義すると、コードが肥大化するのは避けられず、メンテナンスに不利であり、内蔵のミドルウェアを見て、app.UseAuthentication();が簡潔で、aspを見る.Netcoreソース、内蔵ミドルウェアはいずれもミドルウェアクラス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について.Netcoreのミドルウェアとkoa.js,ginミドルウェアは,実装形式がやや異なるが,究極の目標はAOPであり,接面プログラミング向けであり,コード量を低減し,あるルーティングマッチングの方法で同じコードを記述することはない.asp.Netcoreの前はasp.Netの場合、同様のAOP実装もあり、各種FilterAttributeを継承し、属性ルーティングを有効にしたり、カスタム認可フィルタを作成したり、カスタム認証フィルタを作成したり、モデル検証フィルタを作成したりする方法を書き換えます.