ㆍ미들웨어? 사용자가 웹페이지 호출시, 페이지가 열리기전에, 사용자에대한 인증(Authentication) 또는 권한(Authorization) , 웹서버 관련 또는 클라이언트 관련 처리등을 사전에 처리할수 있게 구성하는 전처리? 하는 프로세스로 보면된다. 미들웨어는 파이프라인의 구조를 가지며, 파이프라인에서 미들웨어는 처리완료 후 다음 미들웨어를 델리게이트로 호출해줘야 하며, next() 를 통한 모든 미들웨어 실행완료 호 회귀하여 최초의 미들웨어로 돌아와 next() 다음의 처리를 완료 후 미들웨어는 종료한다. (너무 유명한 그림 -_-) 이 파이프라인에 미들웨어를 추가하여 구성하며, 여러개 등록이 가능하다. 그러므로 미들웨어에따라 처리 순서가 중요하기도하며, A 미들웨어보다 B 미들웨어가 먼저 호출되야 한다거나, A 미들웨어에서 실행한 설정이 하위 모든미들웨어에 영향을 미치기도한다.
기본적으로 asp.net core 의 진입점(엔트리포인트)은 static void main 메서드이며, program.cs 의 에 작성되어있다. 내부에서의 핵심 코드는 UseStartup<T>() 제네릭 메서드가 T 인 startup 클래스, startUp.cs 를 호출하면서 작동된다. ㆍstartUp.cs 구조 (미들웨어의 시작 IApplicationBuilder) Startup 클래스의 구조를 보면 , 크게 3가지로 구분된다. // Startup 생성자는 configuration 객체를 주입(DI) 받게 구성되어 있다., .net core 의 설정파일인 appSetting.json 값들을 IConfiguration 객체로 주입받아서 모든 객체에게 다시 주입할수 있게 되어있다. public Startup(IConfiguration configuration) { Configuration = configuration; } // .net core 에서 필요로하는 서비스들을 등록하고 관리하는 함수 IServiceCollection 객체를 주입받아 // 각종 서비스들을 등록하고 , 미들웨어로 등록될 서비스들의 환경설정 및 옵션들을 설정한다. // 의존성 주입 객체들을 등록하기도 한다. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); } // 미들웨어 추가 , ConfigureServices 메서드에서 등록 & 설정된 서비스들을 실제로 미들웨어 파이프라인에 추가하여 작동시킨다.IApplicationBuildre 객체를 통해 미들웨어를 파이프라인에 추가 하며 3가지 메서드를 지원한다. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // app.Use() ,app.Run() , app.Map() } ㆍ미들웨어 작성 메서드 – 미들웨어가 인자로받는 대리자 들은 모두 비동기로 작동 반드시 async await 활용 app.Use() : 실행할 미들웨어 추가. (context , next) 를 인자로 받으며 , next() 호출시 다음 미들웨어를 연결하여 실행 app.Run() : 실행할 미들웨어 추가 (context) 만 인자로 받으며 app.Run() 실행시 처리 완료 후 미들웨어 단락된다. app.Map() : 호출되는 Url 의 주소를 매핑하기위한 미들웨어로 , URL 호출시 이를 가로채어, Map(“/경로”) 로 설정한 “/경로” 와 같다면 app객체를 인자로 받아, 내부에 App.Use() , App.Run() 로 처리 후 Map() 안에 처리들을 모두 실행후 단락, (Map 만의 미들웨어를 단독으로 구성한다고생각하자) ★ 웹 페이지가 호출될때 마다 매번 미들웨어가 실행된다는것을 잊지말자 ★ app.Use() 만 next() 함수를 가지고있으며 , app.Run() 과 App.Map() 은 다음 미들웨어를 호출할 수 없다 next() 함수 없음. 자신내부의 처리 완료후 미들웨어를 단락시킨다. ★ 다음 실행할 미들웨어가 없을경우 next() 호출하면 클라이언트에서 Connection Reset 200 Ok 오류 발생하므로 호출하지 말것 ★ 더 이상 호출할 미들웨어가 없을 경우 App.Run(); 호출하여 미들웨어롤 종료(단락) 시킬수도 있다. |
ㆍ기본구조 (미들웨어의 인자(대리자)들은 모두 비동기므로, async , await 로 작성해줘야한다.) public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use( async (context , next) => { // app.Use 메서는 context, next 를 인자로 받는다. 반드시 await next() 호출로 다음미들웨어를 호출해줘야 // 계속해서 미들웨어가 실행단다. next() 미호출시 app.Run() 과 같이 미들웨어 터미널(단락)이 된다. }); app.Map(“/TEST” , app => { app.Use( async (context ,next) => { // 처리 context.Response.WriteAsync(“test Map Use #1”); await next(); // app.Map 내부의 다음 미들웨어로 호출한다. “test Map Use #2” 호출 } app.Use(async (context,next) => { // 처리 다음미들웨어가 없으므로 next 를 호출하지않는다. // next 호출시 client 에서 CONNECTION_RESET 오류남 context.Response.WriteAsync(“test Map Use #2”); // await next(); // 다음 미들웨어가 없으므로 호출 하지 않는다.. } // 단, app.Map 에 매핑된 “/TEST” url 호출하여 메서드가 실행시, 단락됨 // 아래 미들웨어들은 실행되지 않음 }); app.Run( async (context) => { context.Response.WriteAsync(“test App.Run”); // app.Run 메서는 context 만 인자로 받는다. 주의, 다음 미들웨어 호출불가, 아래 100개의 미들웨어가 있어도 // 실행되지 않는다. 즉, 미들웨어 터미널(단락)이 된다. // 미들웨어 단락 이 미들웨어가 실행되면, 아래 미들웨어는 실행되지 않음 // 단, app.Run() 메서드가 실행시 단락됨 }); } |
<예제1>app.Use(async (context, next) => { await context.Response.WriteAsync(“Middleware #1“); await next(); });app.Map(“/TEST”, app => { app.Use(async (context, next) => { await context.Response.WriteAsync($”MAP /TEST API call“); }); }); app.Use(async (context, next) => { await context.Response.WriteAsync(“Middleware #2“); await next(); });
app.Map(“/TEST2”, app => { app.Use(async (context, next) => { await context.Response.WriteAsync(“MAP /TEST2 API call“); }); }); app.Run(async (context) => { await context.Response.WriteAsync(“Middleware #3“); }); | 1) 일반(App.Map() 안된) url 호출시 Middleware #1 // next() Middleware #2 // next() Middleware #3 // 단락됨 2) /TEST (App.Map()매핑) url 호출시 Middleware #1 // next() MAP /TEST API call // 단락됨 3) /TEST2 (App.Map()매핑) url 호출시 Middleware #1 // next() Middleware #2 // next() MAP /TEST2 API call // 단락됨 ★Use 와 Run 메서드의경우 조건없이 앱이 실행되면서 무조건 실행하게 되지만, Map 의 경위 Url 의 매핑조건이 만족해야 실행되므로 Map 의 조건에따라 미들웨어 실행범위가 변경된다,(그래서 순서가 중요하다.) |
<예제2> – 아래 소스를 보며 프로세스를 머리속으로 돌려보자, public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.Use(async (context, next) => { context.Response.ContentType = “text/html”; await next(); }); app.Use(async (context, next) => { await context.Response.WriteAsync(“1/3 START app.use FirstM<BR>”); await next(); await context.Response.WriteAsync(“1/4 END app.use FirstM<BR>”); }); app.Use(async (context, next) => { await context.Response.WriteAsync(“2/4 START app.use FirstM<BR>”); await next(); await context.Response.WriteAsync(“2/4 END app.use FirstM<BR>”); }); app.Map(“/TEST”, app => { app.Use(async (context, next) => { var getUserId = context.Request.QueryString.Value; await context.Response.WriteAsync($”MAP /TEST API call UserID {getUserId}<BR>”); // Map 내부에 다음에 실행할 미들웨어가 없기때문에 위에출력도 안되고 , CONNECTION_RESET (javascript) 오류 await next(); }); // Map 함수내 처리 완료후 미들웨어 단락 // 중요한건 현재 app.Map 의 “/TEST” 호출시 내부 처리후 단락된다 그냥 실행시는 “/TEST” 만족하지 않으므로 // 단락되지 않음 }); app.Run(async (context) => { await context.Response.WriteAsync(“3/4 START app.Run FirstM<BR>”); }); // next() 메서드가 없으므로 미들웨어는 터미널(단락) 이 아래로 있는 미들웨어들은 없는것과마찬가지이므로 // App.Run 메서드는 미들웨어를 종료하는 시점의 처리아니면 작성할때 주의 하여야 한다. app.Run(async (context) => // 위에 3/4 app.Run 메서드 실행시 , 먼저 단락 되기 때문에 실행안됨 { await context.Response.WriteAsync(“4/4 <span style=’color:#F00′>app.Run</span> execute app.run<BR>”); }); app.Map(“/TEST2”, app => // 위에 3/4 app.Run 메서드 실행시, 먼저 단락되기 때문에 실행안됨 { app.Use(async (context, next) => { var getUserId = context.Request.QueryString.Value; await context.Response.WriteAsync($”MAP /TEST2 API call UserID {getUserId}<BR>”); // await next(); – Map 메서드 내에 다음 미들웨어가없다 next() 호출 하지않는다. }); }); } |