연습 - 사용자 지정 미들웨어 만들기

완료됨

개발자는 사용자 지정 미들웨어 구성 요소를 만들어 ASP.NET Core 앱에 기능을 추가할 수 있습니다. 사용자 지정 미들웨어는 미들웨어 파이프라인의 어느 곳에나 삽입될 수 있으며 이 예에서 볼 수 있듯이 기본 제공 미들웨어 구성 요소와 함께 사용될 수 있습니다.

파이프라인을 통한 요청 흐름을 보여 주는 다이어그램입니다.

회사의 네트워크 운영 팀에서 프로덕션 환경에서 성능 문제를 해결하고 있습니다. 팀 리더가 앱의 실시간 모니터링을 보다 효과적으로 지원하기 위해 몇 가지 기능을 구현하라는 과제를 맡겼습니다. 앱에서 요청 세부 정보를 콘솔에 기록해야 합니다. 각 요청에 대해 요청 메서드, 경로, 응답 상태 코드를 기록해야 합니다.

이 연습에서는 요청 세부 정보를 콘솔에 기록하는 사용자 지정 미들웨어 구성 요소를 만듭니다.

사용자 지정 미들웨어 추가

기존 ASP.NET Core 앱을 수정하여 요청 세부 정보를 콘솔에 기록하는 사용자 지정 미들웨어를 포함해 보겠습니다.

  1. 아직 열려 있지 않은 경우 Program.cs 파일을 엽니다.

  2. app.Run() 바로 앞에 다음 코드를 삽입합니다.

    app.Use(async (context, next) =>
    {
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
        await next(); 
    });
    

    위의 코드에서

    • app.Use()는 파이프라인에 사용자 지정 미들웨어 구성 요소를 추가합니다. 구성 요소는 HttpContext 개체와 RequestDelegate 개체를 매개 변수로 사용합니다.
    • 대리자는 요청 메서드, 경로, 응답 상태 코드를 콘솔에 기록합니다.
    • await next()는 파이프라인의 다음 미들웨어 구성 요소를 호출합니다.

변경 사항 테스트

  1. Ctrl+Shift+F5를 눌러 앱을 다시 빌드하고 다시 시작합니다.

  2. 브라우저 창이 열리면 루트 URL에 "Welcome to Contoso!"가 표시됨

  3. URL에 /history를 추가하고 Enter 키를 누르십시오. 브라우저가 /about 페이지로 리디렉션됩니다.

  4. Visual Studio Code에서 Ctrl+Shift+P 를 눌러 명령 팔레트를 엽니다. 디버그 콘솔을 검색하여 선택합니다 . 디버그 콘솔 보기에 집중 하여 아래쪽 패널의 디버그 콘솔 탭으로 전환합니다. 다음 줄을 확인합니다.

    GET / 200
    GET /about 200
    

    콘솔 출력에는 각 요청에 대한 요청 메서드, 경로, 응답 상태 코드가 표시됩니다. 첫 번째 줄은 루트 URL에 대한 요청을 보여 주고, 두 번째 줄은 /about 페이지에 대한 요청을 보여 줍니다.

    참고 항목

    브라우저에서 /favicon.ico를 요청할 수도 있습니다. 이는 웹 사이트의 파비콘에 대한 표준 요청이므로 무시할 수 있습니다.

  5. 다음 연습을 위해 앱을 실행해 둡니다.

미들웨어 순서 변경

앱이 작동하는 것처럼 보이지만 문제가 있습니다. /history 페이지를 요청했지만 콘솔 출력에 해당 페이지가 표시되지 않습니다. 이러한 동작은 요청 세부 정보를 기록하는 사용자 지정 미들웨어 구성 요소가 URL 재작성기 미들웨어 뒤에 추가되었기 때문에 발생합니다. URL 재작성기 미들웨어는 /history에서 /about로 요청을 리디렉션하고 응답을 보내지만, 사용자 지정 미들웨어 구성 요소는 요청을 보지 못합니다. 이 문제를 해결해 보겠습니다.

  1. 추가한 app.Use() 줄을 app.UseRewriter() 줄 바로 앞으로 이동합니다.

    전체 Program.cs 파일은 다음과 같습니다.

    using Microsoft.AspNetCore.Rewrite;
    
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.Use(async (context, next) =>
    {
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
        await next(); 
    });
    
    app.UseRewriter(new RewriteOptions().AddRedirect("history", "about"));
    
    app.MapGet("/", () => "Hello World!");
    app.MapGet("/about", () => "Contoso was founded in 2000.");
    
    app.Run();
    

    이제 URL 재작성기 미들웨어 앞에 사용자 지정 미들웨어 구성 요소가 추가되었습니다. 사용자 지정 미들웨어 구성 요소는 URL 재작성기 미들웨어가 요청을 처리하고 리디렉션하기 전에 요청 세부 정보를 기록합니다.

  2. 앱을 다시 시작하고 이전과 같이 테스트합니다. 이번에는 디버그 콘솔 출력에 페이지에 대한 요청이 /history 포함되어야 합니다.

    GET / 200
    GET /history 200
    GET /about 200
    

    이제 콘솔 출력에는 /history 페이지로 리디렉션되기 직전에 /about 페이지에 대한 요청이 표시됩니다.

상태 코드 수정

앱이 거의 준비되었지만, 문제가 하나 더 있습니다. 앱이 요청을 리디렉션하더라도 콘솔 출력의 상태 코드는 항상 200입니다. /history 요청에 대한 상태 코드는 302 리디렉션이어야 합니다. 이런 동작의 이유는 미들웨어 구성 요소가 처리되는 또 다른 순서 문제 때문입니다.

사용자 지정 미들웨어 구성 요소는 세부 정보를 콘솔에 기록한 다음 await next()를 호출하여 다음 미들웨어 구성 요소에 전달합니다. 문제는 StatusCode 개체의 Response 속성이 터미널 미들웨어 구성 요소가 응답을 시작한 후에 설정된다는 것입니다. 이 문제를 해결하려면 코드를 변경해 보겠습니다.

  1. 추가한 대리자에서 Console.WriteLine() 줄을 await next() 줄 뒤로 이동합니다.

    업데이트된 코드는 다음과 같습니다.

    app.Use(async (context, next) =>
    {
        await next(); 
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
    });
    

    이제 사용자 지정 미들웨어 구성 요소는 터미널 미들웨어 구성 요소가 응답 상태 코드를 설정한 후 요청 세부 정보를 기록합니다.

  2. 다시 시작하고 /history 요청을 다시 테스트합니다. 이제 디버그 콘솔 출력에 올바른 상태 코드가 표시됩니다.

    GET / 200
    GET /history 302
    GET /about 200