【比較学習】koa.js、Ginとasp.Netcore——ミドルウェア
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");
});
}
ミドルウェアクラス+ミドルウェア拡張方法+UseXX
Startup.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を継承し、属性ルーティングを有効にしたり、カスタム認可フィルタを作成したり、カスタム認証フィルタを作成したり、モデル検証フィルタを作成したりする方法を書き換えます.