ASP.NET Core에서 오류 처리

작성자: Kirk Larkin, Tom DykstraSteve Smith

이 항목에서는 ASP.NET Core 웹앱에서 오류를 처리하는 일반적인 접근법을 다룹니다. 웹 API는 ASP.NET Core 웹 API에서 오류 처리를 참조하세요.

샘플 코드 보기 또는 다운로드 (다운로드하는 방법) F12 브라우저 개발자 도구의 네트워크 탭은 샘플 앱을 테스트할 때 유용합니다.

개발자 예외 페이지

개발자 예외 페이지에는 처리되지 않은 요청 예외에 대한 자세한 정보가 표시됩니다. ASP.NET Core 템플릿에서 다음 코드를 생성합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

위의 강조 표시된 코드는 앱이 개발 환경에서 실행 중인 경우 개발자 예외 페이지를 사용하도록 설정합니다.

템플릿은 미들웨어 파이프라인의 앞부분에 UseDeveloperExceptionPage를 배치하여, 다음에 오는 미들웨어에서 throw된 미처리 예외를 catch할 수 있도록 합니다.

위의 코드는 앱이 개발 환경에서 실행되는 경우에 개발자 예외 페이지를 사용하도록 설정합니다. 앱이 프로덕션 환경에서 실행되는 경우에는 자세한 예외 정보를 공개적으로 표시해서는 안 됩니다. 환경 구성 방법에 대한 자세한 내용은 ASP.NET Core에서 여러 환경 사용를 참조하세요.

개발자 예외 페이지에는 예외 및 요청에 대한 다음 정보가 포함됩니다.

  • 스택 추적
  • 쿼리 문자열 매개 변수(있는 경우)
  • Cookie(있는 경우)
  • 헤더

예외 처리기 페이지

프로덕션 환경의 사용자 지정 오류 처리 페이지를 구성하려면 UseExceptionHandler를 호출합니다. 이 예외 처리 미들웨어에서 다음을 수행합니다.

  • 미처리 예외를 catch하고 기록합니다.
  • 대체 파이프라인에서 표시된 경로를 사용하여 요청을 다시 실행합니다. 응답이 시작된 경우에는 요청이 다시 실행되지 않습니다. 템플릿 생성 코드는 /Error 경로를 사용하여 요청을 다시 실행합니다.

경고

대체 파이프라인이 자체 예외를 throw하는 경우 예외 처리 미들웨어가 원래 예외를 다시 throw합니다.

다음 예제에서 UseExceptionHandler는 개발 환경이 아닌 환경에서 예외 처리 미들웨어를 추가합니다.

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Pages 앱 템플릿은 Pages 폴더에 오류 페이지(.cshtmlPageModel클래스ErrorModel)를 제공합니다. MVC 앱의 프로젝트 템플릿에는 Home 컨트롤러에 대한 Error 작업 메서드와 오류 보기가 포함됩니다.

예외 처리 미들웨어는 원래 HTTP 메서드를 사용하여 요청을 다시 실행합니다. 오류 처리기 엔드포인트가 특정 HTTP 메서드 집합으로 제한되는 경우 해당 HTTP 메서드에 대해서만 실행됩니다. 예를 들어 [HttpGet] 특성을 사용하는 MVC 컨트롤러 작업은 GET 요청에 대해서만 실행됩니다. 모든 요청이 사용자 지정 오류 처리 페이지에 도달되도록 하려면 특정 HTTP 메서드 집합으로 제한하지 마세요.

원래 HTTP 메서드에 따라 예외를 서로 다르게 처리하려면 다음을 수행합니다.

  • Razor Pages의 경우 여러 처리기 메서드를 만듭니다. 예를 들어 OnGet을 사용하여 GET 예외를 처리하고 OnPost를 사용하여 POST 예외를 처리합니다.
  • MVC의 경우 여러 작업에 HTTP 동사 특성을 적용합니다. 예를 들어 [HttpGet]을 사용하여 GET 예외를 처리하고 [HttpPost]를 사용하여 POST 예외를 처리합니다.

인증되지 않은 사용자가 사용자 지정 오류 처리 페이지를 볼 수 있도록 하려면 익명 액세스를 지원해야 합니다.

예외에 액세스

IExceptionHandlerPathFeature를 사용하여 오류 처리기에서 예외 및 원래 요청 경로에 액세스합니다. 다음 코드에서는 ASP.NET Core 템플릿에서 생성된 기본 Pages/Error.cshtml.csExceptionMessage를 추가합니다.

[ResponseCache(Duration=0, Location=ResponseCacheLocation.None, NoStore=true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }
    private readonly ILogger<ErrorModel> _logger;

    public ErrorModel(ILogger<ErrorModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
        HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "File error thrown";
            _logger.LogError(ExceptionMessage);
        }
        if (exceptionHandlerPathFeature?.Path == "/index")
        {
            ExceptionMessage += " from home page";
        }
    }
}

경고

클라이언트에 중요한 오류 정보를 제공하지 마세요. 오류 제공은 보안 위험입니다.

샘플 앱에서 예외를 테스트하려면 다음을 수행합니다.

  • 환경을 프로덕션으로 설정합니다.
  • Program.cswebBuilder.UseStartup<Startup>();에서 주석을 제거합니다.
  • 홈페이지에서 예외 트리거 를 선택합니다.

예외 처리기 람다

사용자 지정 예외 처리기 페이지의 대안은 UseExceptionHandler에 람다를 제공하는 것입니다. 람다를 사용하면 응답을 반환하기 전에 오류에 액세스할 수 있습니다.

다음 코드에서는 예외 처리에 람다를 사용합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorApp =>
        {
            errorApp.Run(async context =>
            {
                context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;;
                context.Response.ContentType = "text/html";

                await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
                await context.Response.WriteAsync("ERROR!<br><br>\r\n");

                var exceptionHandlerPathFeature =
                    context.Features.Get<IExceptionHandlerPathFeature>();

                if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
                {
                    await context.Response.WriteAsync(
                                              "File error thrown!<br><br>\r\n");
                }

                await context.Response.WriteAsync(
                                              "<a href=\"/\">Home</a><br>\r\n");
                await context.Response.WriteAsync("</body></html>\r\n");
                await context.Response.WriteAsync(new string(' ', 512)); 
            });
        });
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

경고

IExceptionHandlerFeature 또는 IExceptionHandlerPathFeature에서 클라이언트에 중요한 오류 정보를 제공하지 마세요. 오류 제공은 보안 위험입니다.

샘플 앱에서 예외 처리 람다를 테스트하려면 다음을 수행합니다.

  • 환경을 프로덕션으로 설정합니다.
  • Program.cswebBuilder.UseStartup<StartupLambda>();에서 주석을 제거합니다.
  • 홈페이지에서 예외 트리거 를 선택합니다.

UseStatusCodePages

기본적으로 ASP.NET Core 앱은 ‘404 - 찾을 수 없음’과 같은 HTTP 오류 상태 코드에 대한 상태 코드 페이지를 제공하지 않습니다. 앱에서 본문이 없는 HTTP 400-599 오류 상태 코드가 발생하면 상태 코드와 빈 응답 본문이 반환됩니다. 상태 코드 페이지를 제공하려면 상태 코드 페이지 미들웨어를 사용합니다. 일반적인 오류 상태 코드에 대해 기본 텍스트 전용 처리기를 사용하려면 Startup.Configure 메서드에서 UseStatusCodePages를 호출합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages();

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

요청 처리 미들웨어보다 먼저 UseStatusCodePages를 호출합니다. 예를 들어 정적 파일 미들웨어 및 엔드포인트 미들웨어보다 먼저 UseStatusCodePages를 호출합니다.

UseStatusCodePages를 사용하지 않는 경우 엔드포인트가 없는 URL로 이동하면 엔드포인트를 찾을 수 없다는 브라우저 종속 오류 메시지가 반환됩니다. 예를 들어 Home/Privacy2로 이동하는 경우 UseStatusCodePages를 호출하면 브라우저에서 다음을 반환합니다.

Status Code: 404; Not Found

UseStatusCodePages는 사용자에게 유용하지 않은 메시지를 반환하기 때문에 일반적으로 프로덕션에서 사용되지 않습니다.

샘플 앱에서 UseStatusCodePages를 테스트하려면 다음을 수행합니다.

  • 환경을 프로덕션으로 설정합니다.
  • Program.cswebBuilder.UseStartup<StartupUseStatusCodePages>();에서 주석을 제거합니다.
  • 홈페이지에서 링크를 선택합니다.

참고

상태 코드 페이지 미들웨어는 예외를 catch하지 않습니다. 사용자 지정 오류 처리 페이지를 제공하려면 예외 처리기 페이지를 사용하세요.

형식 문자열을 사용하는 UseStatusCodePages

응답 콘텐츠 형식 및 텍스트를 사용자 지정하려면 콘텐츠 형식 및 형식 문자열을 사용하는 UseStatusCodePages의 오버로드를 사용합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages(
        "text/plain", "Status code page, status code: {0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

위의 코드에서 {0}은 오류 코드에 대한 자리 표시자입니다.

형식 문자열이 있는 UseStatusCodePages는 사용자에게 유용하지 않은 메시지를 반환하기 때문에 일반적으로 프로덕션에서 사용되지 않습니다.

샘플 앱에서 UseStatusCodePages를 테스트하려면 Program.cswebBuilder.UseStartup<StartupFormat>();에서 주석을 제거합니다.

람다를 사용하는 UseStatusCodePages

사용자 지정 오류 처리 및 응답 쓰기 코드를 지정하려면 람다 식을 사용하는 UseStatusCodePages의 오버로드를 사용합니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePages(async context =>
    {
        context.HttpContext.Response.ContentType = "text/plain";

        await context.HttpContext.Response.WriteAsync(
            "Status code page, status code: " +
            context.HttpContext.Response.StatusCode);
    });

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

람다를 사용하는 UseStatusCodePages는 사용자에게 유용하지 않은 메시지를 반환하기 때문에 일반적으로 프로덕션에서 사용되지 않습니다.

샘플 앱에서 UseStatusCodePages를 테스트하려면 Program.cswebBuilder.UseStartup<StartupStatusLambda>();에서 주석을 제거합니다.

UseStatusCodePagesWithRedirects

UseStatusCodePagesWithRedirects 확장 메서드는:

  • ‘302 - 찾음’ 상태 코드를 클라이언트에 보냅니다.
  • URL 템플릿에 제공된 오류 처리 엔드포인트로 클라이언트를 리디렉션합니다. 오류 처리 엔드포인트는 일반적으로 오류 정보를 표시하고 HTTP 200을 반환합니다.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithRedirects("/MyStatusCode?code={0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

위의 코드에 표시된 것처럼 URL 템플릿에는 상태 코드에 대한 {0} 자리 표시자가 포함될 수 있습니다. URL 템플릿이 ~(물결표)로 시작하는 경우 ~는 앱의 PathBase로 대체됩니다. 앱에서 엔드포인트를 지정할 때 해당 엔드포인트에 대한 MVC 보기 또는 Razor 페이지를 만듭니다. Razor Pages 예제는 샘플 앱Pages/MyStatusCode.cshtml을 참조하세요.

이 메서드는 일반적으로 앱이 다음과 같은 경우에 사용됩니다.

  • 일반적으로 다른 앱이 오류를 처리하는 상황에서 앱이 클라이언트를 다른 엔드포인트로 리디렉션해야 하는 경우. 웹앱의 경우 클라이언트의 브라우저 주소 표시줄에 리디렉션된 엔드포인트가 반영됩니다.
  • 초기 리디렉션 응답과 함께 원래 상태 코드를 유지하고 반환하면 안 되는 경우.

샘플 앱에서 UseStatusCodePages를 테스트하려면 Program.cswebBuilder.UseStartup<StartupSCredirect>();에서 주석을 제거합니다.

UseStatusCodePagesWithReExecute

UseStatusCodePagesWithReExecute 확장 메서드는:

  • 원래 상태 코드를 클라이언트에 반환합니다.
  • 대체 경로에서 요청 파이프라인을 다시 실행하여 응답 본문을 생성합니다.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseStatusCodePagesWithReExecute("/MyStatusCode2", "?code={0}");

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

앱 내에 엔드포인트가 지정된 경우 해당 엔드포인트에 대한 MVC 보기 또는 Razor 페이지를 만듭니다. 요청이 상태 페이지로 다시 라우팅될 수 있도록 UseRouting 전에 UseStatusCodePagesWithReExecute를 배치합니다. Razor Pages 예제는 샘플 앱Pages/MyStatusCode2.cshtml을 참조하세요.

이 메서드는 일반적으로 앱이 다음과 같은 경우에 사용됩니다.

  • 다른 엔드포인트로 리디렉션하지 않고 요청을 처리해야 하는 경우. 웹앱의 경우 클라이언트의 브라우저 주소 표시줄에 원래 요청된 엔드포인트가 반영됩니다.
  • 원래 상태 코드를 유지하고 응답과 함께 반환해야 하는 경우.

URL 및 쿼리 문자열 템플릿에는 상태 코드에 대한 자리 표시자({0})가 포함될 수 있습니다. URL 템플릿은 /로 시작해야 합니다.

오류를 처리하는 엔드포인트는 다음 예제와 같이 오류를 생성한 원래 URL을 가져올 수 있습니다.

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class MyStatusCode2Model : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public string ErrorStatusCode { get; set; }

    public string OriginalURL { get; set; }
    public bool ShowOriginalURL => !string.IsNullOrEmpty(OriginalURL);

    public void OnGet(string code)
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
        ErrorStatusCode = code;

        var statusCodeReExecuteFeature = HttpContext.Features.Get<
                                               IStatusCodeReExecuteFeature>();
        if (statusCodeReExecuteFeature != null)
        {
            OriginalURL =
                statusCodeReExecuteFeature.OriginalPathBase
                + statusCodeReExecuteFeature.OriginalPath
                + statusCodeReExecuteFeature.OriginalQueryString;
        }
    }
}

Razor Pages 예제는 샘플 앱Pages/MyStatusCode2.cshtml을 참조하세요.

샘플 앱에서 UseStatusCodePages를 테스트하려면 Program.cswebBuilder.UseStartup<StartupSCreX>();에서 주석을 제거합니다.

상태 코드 페이지 사용 안 함

MVC 컨트롤러 또는 작업 메서드에 대한 상태 코드 페이지를 비활성화하려면 [SkipStatusCodePages] 특성을 사용합니다.

Razor Pages 처리기 메서드 또는 MVC 컨트롤러에서 특정 요청에 대한 상태 코드 페이지를 사용하지 않으려면 IStatusCodePagesFeature를 사용합니다.

public void OnGet()
{
    // using Microsoft.AspNetCore.Diagnostics;
    var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

    if (statusCodePagesFeature != null)
    {
        statusCodePagesFeature.Enabled = false;
    }
}

예외 처리 코드

예외 처리 페이지의 코드도 예외를 throw할 수 있습니다. 프로덕션 오류 페이지는 철저히 테스트하고 자체적으로 예외를 throw하지 않도록 특히 주의해야 합니다.

응답 헤더

또한 응답의 헤더가 전송되고 나면 다음 제한이 적용됩니다.

  • 앱에서 응답의 상태 코드를 변경할 수 없습니다.
  • 예외 페이지 또는 처리기를 실행할 수 없습니다. 응답을 완료하거나 연결이 중단되어야 합니다.

서버 예외 처리

앱의 예외 처리 논리 외에도 HTTP 서버 구현에서 몇 가지 예외를 처리할 수 있습니다. 응답 헤더가 전송되기 전에 예외를 catch한 서버는 응답 본문 없이 500 - Internal Server Error 응답을 보냅니다. 응답 헤더가 전송된 후에 예외를 catch한 서버는 연결을 닫습니다. 앱에서 처리되지 않는 요청은 서버에서 처리됩니다. 서버에서 요청을 처리할 때 발생하는 모든 예외는 서버의 예외 처리에 의해 처리됩니다. 앱의 사용자 지정 오류 페이지, 예외 처리 미들웨어 및 필터는 이 동작에 영향을 미치지 않습니다.

시작 예외 처리

호스팅 계층만 앱 시작 시 발생하는 예외를 처리할 수 있습니다. 시작 오류를 캡처하고 자세한 오류를 캡처하도록 호스트를 구성할 수 있습니다.

호스팅 계층은 호스트 주소/포트 바인딩 후에 오류가 발생하는 경우에만 캡처된 시작 오류에 대한 오류 페이지만 표시할 수 있습니다. 바인딩이 실패하면 결과는 다음과 같습니다.

  • 호스팅 계층에서 심각한 예외를 기록합니다.
  • dotnet 프로세스의 작동이 중단됩니다.
  • HTTP 서버가 Kestrel인 경우 오류 페이지가 표시되지 않습니다.

IIS(또는 Azure App Service) 또는 IIS Express에서 실행 중일 때, 프로세스를 시작할 수 없는 경우 ASP.NET Core 모듈이 ‘502.5 - 프로세스 실패’를 반환합니다. 자세한 내용은 Azure App Service 및 IIS에서 ASP.NET Core 문제 해결를 참조하세요.

데이터베이스 오류 페이지

데이터베이스 개발자 페이지 예외 필터 AddDatabaseDeveloperPageExceptionFilter는 Entity Framework Core 마이그레이션을 사용하여 해결할 수 있는 데이터베이스 관련 예외를 캡처합니다. 이 예외가 발생하면 문제 해결을 위해 가능한 작업의 세부 정보가 포함된 HTML 응답이 생성됩니다. 이 페이지는 개발 환경에서만 사용할 수 있습니다. 다음 코드는 개별 사용자 계정을 지정할 때 ASP.NET Core Razor 페이지 템플릿에서 생성되었습니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();
}

예외 필터

MVC 앱에서는 예외 필터를 전역으로 구성하거나 컨트롤러별 또는 작업별로 구성할 수 있습니다. Razor Pages 앱에서는 전역으로 구성하거나 페이지 모델별로 구성할 수 있습니다. 이러한 필터는 컨트롤러 작업 또는 다른 필터를 실행하는 동안 발생하는 처리되지 않은 예외를 처리합니다. 자세한 내용은 ASP.NET Core에서 필터링를 참조하세요.

예외 필터는 MVC 작업 내에서 발생하는 예외를 트래핑하는 데 유용하지만 기본 제공 예외 처리 미들웨어 UseExceptionHandler만큼 유연하지는 않습니다. 선택한 MVC 작업에 따라 오류 처리를 다르게 수행해야 하는 경우에만 UseExceptionHandler를 사용하는 것이 좋습니다.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

모델 상태 오류

모델 상태 오류를 처리하는 방법에 대한 자세한 내용은 모델 바인딩모델 유효성 검사를 참조하세요.

추가 자료

작성자: Tom DykstraSteve Smith

이 항목에서는 ASP.NET Core 웹앱에서 오류를 처리하는 일반적인 접근법을 다룹니다. 웹 API는 ASP.NET Core 웹 API에서 오류 처리를 참조하세요.

샘플 코드 보기 또는 다운로드 (다운로드하는 방법)

개발자 예외 페이지

개발자 예외 페이지에는 요청 예외에 대한 자세한 정보가 표시됩니다. ASP.NET Core 템플릿에서 다음 코드를 생성합니다.

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

위의 코드는 앱이 개발 환경에서 실행 중인 경우 개발자 예외 페이지를 사용하도록 설정합니다.

템플릿은 미들웨어 앞에 UseDeveloperExceptionPage를 배치하여 다음에 오는 미들웨어에서 예외를 catch할 수 있도록 합니다.

위의 코드에서는 앱이 개발 환경에서 실행 중인 경우에만 개발자 예외 페이지를 사용하도록 설정합니다. 앱이 프로덕션에서 실행되는 경우에는 자세한 예외 정보를 공개적으로 표시해서는 안 됩니다. 환경 구성 방법에 대한 자세한 내용은 ASP.NET Core에서 여러 환경 사용를 참조하세요.

개발자 예외 페이지에는 예외 및 요청에 대한 다음 정보가 포함됩니다.

  • 스택 추적
  • 쿼리 문자열 매개 변수(있는 경우)
  • Cookie(있는 경우)
  • 헤더

예외 처리기 페이지

프로덕션 환경의 사용자 지정 오류 처리 페이지를 구성하려면 예외 처리 미들웨어를 사용합니다. 이 미들웨어는 다음을 수행합니다.

  • 예외를 잡고 기록합니다.
  • 표시된 페이지 또는 컨트롤러에 대한 대체 파이프라인에서 요청을 다시 실행합니다. 응답이 시작된 경우에는 요청이 다시 실행되지 않습니다. 템플릿 생성 코드는 /Error에 대한 요청을 다시 실행합니다.

다음 예제에서 UseExceptionHandler는 비 개발 환경에서 예외 처리 미들웨어를 추가합니다.

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

Razor Pages 앱 템플릿은 Pages 폴더에 오류 페이지( .cshtml) 및 PageModel 클래스(ErrorModel)를 제공합니다. MVC 앱의 프로젝트 템플릿에는 Home 컨트롤러에 대한 오류 작업 메서드와 오류 보기가 포함됩니다.

오류 처리기 작업 메서드를 HttpGet와 같은 HTTP 메서드 특성을 사용하여 표시하지 마세요. 명시적 동사는 일부 요청이 메서드에 도달하지 못하게 방해합니다. 인증되지 않은 사용자에게 오류 보기를 표시해야 하는 경우 메서드에 대한 익명 액세스를 허용합니다.

예외에 액세스

IExceptionHandlerPathFeature를 사용하여 오류 처리기 컨트롤러 또는 페이지에서 예외 및 원래 요청 경로에 액세스합니다.

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
    public string RequestId { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    public string ExceptionMessage { get; set; }

    public void OnGet()
    {
        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;

        var exceptionHandlerPathFeature =
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
        {
            ExceptionMessage = "File error thrown";
        }
        if (exceptionHandlerPathFeature?.Path == "/index")
        {
            ExceptionMessage += " from home page";
        }
    }
}

경고

클라이언트에 중요한 오류 정보를 제공하지 마세요. 오류 제공은 보안 위험입니다.

위의 예외 처리 페이지를 트리거하려면 환경을 프로덕션으로 설정하고 예외를 적용합니다.

예외 처리기 람다

사용자 지정 예외 처리기 페이지의 대안은 UseExceptionHandler에 람다를 제공하는 것입니다. 람다를 사용하면 응답을 반환하기 전에 오류에 액세스할 수 있습니다.

다음은 예외 처리를 위해 람다를 사용하는 예제입니다.

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
   app.UseExceptionHandler(errorApp =>
   {
        errorApp.Run(async context =>
        {
            context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
            context.Response.ContentType = "text/html";

            await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
            await context.Response.WriteAsync("ERROR!<br><br>\r\n");

            var exceptionHandlerPathFeature = 
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync("File error thrown!<br><br>\r\n");
            }

            await context.Response.WriteAsync("<a href=\"/\">Home</a><br>\r\n");
            await context.Response.WriteAsync("</body></html>\r\n");
            await context.Response.WriteAsync(new string(' ', 512)); // IE padding
        });
    });
    app.UseHsts();
}

위의 코드에서 Internet Explorer 브라우저에 IE 오류 메시지가 아닌 오류 메시지가 표시되도록 await context.Response.WriteAsync(new string(' ', 512));가 추가됩니다. 자세한 내용은 이 GitHub 이슈를 참조하세요.

경고

IExceptionHandlerFeature 또는 IExceptionHandlerPathFeature에서 클라이언트에 중요한 오류 정보를 제공하지 마세요. 오류 제공은 보안 위험입니다.

샘플 앱에서 예외 처리 람다의 결과를 확인하려면 ProdEnvironmentErrorHandlerLambda 전처리기 지시문을 사용하고 홈페이지에서 Trigger an exception 을 선택합니다.

UseStatusCodePages

기본적으로 ASP.NET Core 앱은 ‘404 - 찾을 수 없음’과 같은 HTTP 상태 코드에 대한 상태 코드 페이지를 제공하지 않습니다. 앱은 상태 코드와 빈 응답 본문을 반환합니다. 상태 코드 페이지를 제공하려면 상태 코드 페이지 미들웨어를 사용합니다.

미들웨어는 Microsoft.AspNetCore.Diagnostics 패키지를 통해 사용할 수 있습니다.

일반적인 오류 상태 코드에 대해 기본 텍스트 전용 처리기를 사용하려면 Startup.Configure 메서드에서 UseStatusCodePages를 호출합니다.

app.UseStatusCodePages();

요청 처리 미들웨어(예: 정적 파일 미들웨어 및 MVC 미들웨어) 이전에 UseStatusCodePages를 호출합니다.

UseStatusCodePages를 사용하지 않는 경우 엔드포인트가 없는 URL로 이동하면 엔드포인트를 찾을 수 없다는 브라우저 종속 오류 메시지가 반환됩니다. 예를 들어 Home/Privacy2로 이동하는 경우 UseStatusCodePages를 호출하면 브라우저에서 다음을 반환합니다.

Status Code: 404; Not Found

형식 문자열을 사용하는 UseStatusCodePages

응답 콘텐츠 형식 및 텍스트를 사용자 지정하려면 콘텐츠 형식 및 형식 문자열을 사용하는 UseStatusCodePages의 오버로드를 사용합니다.

app.UseStatusCodePages(
    "text/plain", "Status code page, status code: {0}");            

람다를 사용하는 UseStatusCodePages

사용자 지정 오류 처리 및 응답 쓰기 코드를 지정하려면 람다 식을 사용하는 UseStatusCodePages의 오버로드를 사용합니다.


app.UseStatusCodePages(async context =>
{
    context.HttpContext.Response.ContentType = "text/plain";

    await context.HttpContext.Response.WriteAsync(
        "Status code page, status code: " + 
        context.HttpContext.Response.StatusCode);
});

UseStatusCodePagesWithRedirects

UseStatusCodePagesWithRedirects 확장 메서드는:

  • ‘302 - 찾음’ 상태 코드를 클라이언트에 보냅니다.
  • 클라이언트를 URL 템플릿에 제공된 위치로 리디렉션합니다.
app.UseStatusCodePagesWithRedirects("/StatusCode?code={0}");

예제에서 볼 수 있는 것과 같이 URL 템플릿에는 상태 코드에 대한 {0} 자리 표시자가 포함될 수 있습니다. URL 템플릿이 ~(물결표)로 시작하는 경우 ~는 앱의 PathBase로 대체됩니다. 앱 내의 엔드포인트를 가리키는 경우 해당 엔드포인트에 대한 MVC 뷰 또는 Razor 페이지를 만들어야 합니다. Razor Pages 예제를 보려면 샘플 앱Pages/StatusCode.cshtml 을 참조하세요.

이 메서드는 일반적으로 앱이 다음과 같은 경우에 사용됩니다.

  • 일반적으로 다른 앱이 오류를 처리하는 상황에서 앱이 클라이언트를 다른 엔드포인트로 리디렉션해야 하는 경우. 웹앱의 경우 클라이언트의 브라우저 주소 표시줄에 리디렉션된 엔드포인트가 반영됩니다.
  • 초기 리디렉션 응답과 함께 원래 상태 코드를 유지하고 반환하면 안 되는 경우.

UseStatusCodePagesWithReExecute

UseStatusCodePagesWithReExecute 확장 메서드는:

  • 원래 상태 코드를 클라이언트에 반환합니다.
  • 대체 경로에서 요청 파이프라인을 다시 실행하여 응답 본문을 생성합니다.
app.UseStatusCodePagesWithReExecute("/StatusCode","?code={0}");

앱 내의 엔드포인트를 가리키는 경우 해당 엔드포인트에 대한 MVC 뷰 또는 Razor 페이지를 만들어야 합니다. 요청이 상태 페이지로 다시 라우팅될 수 있도록 UseRouting 전에 UseStatusCodePagesWithReExecute를 배치합니다. Razor Pages 예제를 보려면 샘플 앱Pages/StatusCode.cshtml 을 참조하세요.

이 메서드는 일반적으로 앱이 다음과 같은 경우에 사용됩니다.

  • 다른 엔드포인트로 리디렉션하지 않고 요청을 처리해야 하는 경우. 웹앱의 경우 클라이언트의 브라우저 주소 표시줄에 원래 요청된 엔드포인트가 반영됩니다.
  • 원래 상태 코드를 유지하고 응답과 함께 반환해야 하는 경우.

URL 및 쿼리 문자열 템플릿에는 상태 코드에 대한 자리 표시자({0})가 포함될 수 있습니다. URL 템플릿은 슬래시(/)로 시작해야 합니다. 경로에서 자리 표시자를 사용하는 경우, 엔드포인트(페이지 또는 컨트롤러)가 경로 세그먼트를 처리할 수 있는지 확인하세요. 예를 들어 오류를 위한 Razor 페이지는 @page 지시문을 사용하여 선택적 경로 세그먼트 값을 수락해야 합니다.

@page "{code?}"

오류를 처리하는 엔드포인트는 다음 예제와 같이 오류를 생성한 원래 URL을 가져올 수 있습니다.

var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
if (statusCodeReExecuteFeature != null)
{
    OriginalURL =
        statusCodeReExecuteFeature.OriginalPathBase
        + statusCodeReExecuteFeature.OriginalPath
        + statusCodeReExecuteFeature.OriginalQueryString;
}

오류 처리기 작업 메서드를 HttpGet와 같은 HTTP 메서드 특성을 사용하여 표시하지 마세요. 명시적 동사는 일부 요청이 메서드에 도달하지 못하게 방해합니다. 인증되지 않은 사용자에게 오류 보기를 표시해야 하는 경우 메서드에 대한 익명 액세스를 허용합니다.

상태 코드 페이지 사용 안 함

MVC 컨트롤러 또는 작업 메서드에 대한 상태 코드 페이지를 비활성화하려면 [SkipStatusCodePages] 특성을 사용합니다.

Razor Pages 처리기 메서드 또는 MVC 컨트롤러에서 특정 요청에 대한 상태 코드 페이지를 사용하지 않으려면 IStatusCodePagesFeature를 사용합니다.

var statusCodePagesFeature = HttpContext.Features.Get<IStatusCodePagesFeature>();

if (statusCodePagesFeature != null)
{
    statusCodePagesFeature.Enabled = false;
}

예외 처리 코드

예외 처리 페이지의 코드도 예외를 던질 수 있습니다. 프로덕션 오류 페이지를 완전한 정적 콘텐츠로 구성하는 것은 종종 좋은 생각입니다.

응답 헤더

또한 응답의 헤더가 전송되고 나면 다음 제한이 적용됩니다.

  • 앱에서 응답의 상태 코드를 변경할 수 없습니다.
  • 예외 페이지 또는 처리기를 실행할 수 없습니다. 응답을 완료하거나 연결이 중단되어야 합니다.

서버 예외 처리

앱의 예외 처리 논리 외에도 HTTP 서버 구현에서 몇 가지 예외를 처리할 수 있습니다. 응답 헤더가 전송되기 전에 예외를 catch한 서버는 응답 본문 없이 ‘500 - 내부 서버 오류’ 응답을 보냅니다. 응답 헤더가 전송된 후에 예외를 catch한 서버는 연결을 닫습니다. 앱으로 처리되지 않는 요청은 서버에서 처리됩니다. 서버에서 요청을 처리할 때 발생하는 모든 예외는 서버의 예외 처리에 의해 처리됩니다. 앱의 사용자 지정 오류 페이지, 예외 처리 미들웨어 및 필터는 이 동작에 영향을 미치지 않습니다.

시작 예외 처리

호스팅 계층만 앱 시작 시 발생하는 예외를 처리할 수 있습니다. 시작 오류를 캡처하고 자세한 오류를 캡처하도록 호스트를 구성할 수 있습니다.

호스팅 계층은 호스트 주소/포트 바인딩 후에 오류가 발생하는 경우에만 캡처된 시작 오류에 대한 오류 페이지만 표시할 수 있습니다. 바인딩이 실패하면 결과는 다음과 같습니다.

  • 호스팅 계층에서 심각한 예외를 기록합니다.
  • dotnet 프로세스의 작동이 중단됩니다.
  • HTTP 서버가 Kestrel인 경우 오류 페이지가 표시되지 않습니다.

IIS(또는 Azure App Service) 또는 IIS Express에서 실행 중일 때, 프로세스를 시작할 수 없는 경우 ASP.NET Core 모듈이 ‘502.5 - 프로세스 실패’를 반환합니다. 자세한 내용은 Azure App Service 및 IIS에서 ASP.NET Core 문제 해결를 참조하세요.

데이터베이스 오류 페이지

데이터베이스 오류 페이지 미들웨어는 Entity Framework 마이그레이션을 사용하여 해결할 수 있는 데이터베이스 관련 예외를 잡습니다. 이 예외가 발생하면 문제 해결을 위한 가능한 작업의 세부 정보가 포함된 HTML 응답이 생성됩니다. 이 페이지는 개발 환경에서만 사용하도록 설정해야 합니다. Startup.Configure에 코드를 추가하여 페이지를 사용하도록 설정합니다.

if (env.IsDevelopment())
{
    app.UseDatabaseErrorPage();
}

UseDatabaseErrorPage에는 Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet 패키지가 필요합니다.

예외 필터

MVC 앱에서는 예외 필터를 전역으로 구성하거나 컨트롤러별 또는 작업별로 구성할 수 있습니다. Razor Pages 앱에서는 전역으로 구성하거나 페이지 모델별로 구성할 수 있습니다. 이러한 필터는 컨트롤러 작업 또는 다른 필터를 실행하는 동안 발생하는 처리되지 않은 예외를 처리합니다. 자세한 내용은 ASP.NET Core에서 필터링를 참조하세요.

예외 필터는 MVC 작업 내에서 발생하는 예외를 트래핑하는 데 유용하지만 예외 처리 미들웨어만큼 유연하지는 않습니다. 미들웨어를 사용하는 것이 좋습니다. 선택한 MVC 작업에 따라 오류 처리를 다르게 수행해야 하는 경우에만 필터를 사용하세요.

모델 상태 오류

모델 상태 오류를 처리하는 방법에 대한 자세한 내용은 모델 바인딩모델 유효성 검사를 참조하세요.

추가 자료