Asp.net core Middleware(미들웨어)

Asp.net Core Middleware 

ㆍ미들웨어?
사용자가 웹페이지 호출시, 페이지가 열리기전에, 사용자에대한 인증(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() 호출 하지않는다.
         });
     });

}

 

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다