ASP.NET Web API 예외 처리

이 문서에서는 ASP.NET Web API 오류 및 예외 처리에 대해 설명합니다.

HttpResponseException

Web API 컨트롤러가 catch되지 않은 예외를 throw하면 어떻게 됩니까? 기본적으로 대부분의 예외는 상태 코드 500 내부 서버 오류를 사용하여 HTTP 응답으로 변환됩니다.

HttpResponseException 형식은 특별한 경우입니다. 이 예외는 예외 생성자에서 지정한 HTTP 상태 코드를 반환합니다. 예를 들어 id 매개 변수가 유효하지 않으면 다음 메서드는 404를 반환합니다. 찾을 수 없습니다.

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return item;
}

응답에 대한 더 많은 제어를 위해 전체 응답 메시지를 생성하고 HttpResponseException에 포함할 수도 있습니다.

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            Content = new StringContent(string.Format("No product with ID = {0}", id)),
            ReasonPhrase = "Product ID Not Found"
        };
        throw new HttpResponseException(resp);
    }
    return item;
}

예외 필터

예외 필터를 작성하여 Web API에서 예외를 처리하는 방법을 사용자 지정할 수 있습니다. 예외 필터는 컨트롤러 메서드가 HttpResponseException 예외가 아닌 처리되지 않은 예외를 throw할 때 실행됩니다. HttpResponseException 형식은 HTTP 응답을 반환하기 위해 특별히 설계되었기 때문에 특별한 경우입니다.

예외 필터는 System.Web.Http.Filters.IExceptionFilter 인터페이스를 구현합니다 . 예외 필터를 작성하는 가장 간단한 방법은 System.Web.Http.Filters.ExceptionFilterAttribute 클래스에서 파생되고 OnException 메서드를 재정의하는 것입니다.

참고

ASP.NET Web API 예외 필터는 ASP.NET MVC의 예외 필터와 비슷합니다. 그러나 별도의 네임스페이스 및 함수에서 별도로 선언됩니다. 특히 MVC에서 사용되는 HandleErrorAttribute 클래스는 Web API 컨트롤러에서 throw된 예외를 처리하지 않습니다.

NotImplementedException 예외를 HTTP 상태 코드 501, 구현되지 않음으로 변환하는 필터는 다음과 같습니다.

namespace ProductStore.Filters
{
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http.Filters;

    public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute 
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception is NotImplementedException)
            {
                context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
            }
        }
    }
}

HttpActionExecutedContext 개체의 Response 속성에는 클라이언트로 전송될 HTTP 응답 메시지가 포함됩니다.

예외 필터 등록

다음과 같은 여러 가지 방법으로 Web API 예외 필터를 등록할 수 있습니다.

  • 작업 기준
  • 컨트롤러 기준
  • 전역적으로

특정 작업에 필터를 적용하려면 필터를 작업에 특성으로 추가합니다.

public class ProductsController : ApiController
{
    [NotImplExceptionFilter]
    public Contact GetContact(int id)
    {
        throw new NotImplementedException("This method is not implemented");
    }
}

컨트롤러의 모든 작업에 필터를 적용하려면 컨트롤러 클래스에 필터를 특성으로 추가합니다.

[NotImplExceptionFilter]
public class ProductsController : ApiController
{
    // ...
}

모든 Web API 컨트롤러에 필터를 전역적으로 적용하려면 필터의 instance GlobalConfiguration.Configuration.Filters 컬렉션에 추가합니다. 이 컬렉션의 예외 필터는 모든 Web API 컨트롤러 작업에 적용됩니다.

GlobalConfiguration.Configuration.Filters.Add(
    new ProductStore.NotImplExceptionFilterAttribute());

"ASP.NET MVC 4 웹 애플리케이션" 프로젝트 템플릿을 사용하여 프로젝트를 만드는 경우 Web API 구성 코드를 App_Start 폴더에 있는 클래스 내에 WebApiConfig 배치합니다.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute());

        // Other configuration code...
    }
}

HttpError

HttpError 개체는 응답 본문에 오류 정보를 반환하는 일관된 방법을 제공합니다. 다음 예제에서는 응답 본문에서 HttpError를 사용하여 HTTP 상태 코드 404(찾을 수 없음)를 반환하는 방법을 보여 줍니다.

public HttpResponseMessage GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.OK, item);
    }
}

CreateErrorResponseSystem.Net.Http.HttpRequestMessageExtensions 클래스에 정의된 확장 메서드입니다. 내부적으로 CreateErrorResponseHttpError instance 만든 다음 HttpError를 포함하는 HttpResponseMessage를 만듭니다.

이 예제에서 메서드가 성공하면 HTTP 응답에서 제품을 반환합니다. 그러나 요청된 제품을 찾을 수 없는 경우 HTTP 응답은 요청 본문에 HttpError 를 포함합니다. 응답은 다음과 같을 수 있습니다.

HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51

{
  "Message": "Product with id = 12 not found"
}

이 예제에서는 HttpError 가 JSON으로 직렬화되었습니다. HttpError를 사용할 때의 장점 중 하나는 강력한 형식의 다른 모델과 동일한 콘텐츠 협상 및 serialization 프로세스를 거치는 것입니다.

HttpError 및 모델 유효성 검사

모델 유효성 검사를 위해 모델 상태를 CreateErrorResponse에 전달하여 응답에 유효성 검사 오류를 포함할 수 있습니다.

public HttpResponseMessage PostProduct(Product item)
{
    if (!ModelState.IsValid)
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }

    // Implementation not shown...
}

이 예제에서는 다음 응답을 반환할 수 있습니다.

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 320

{
  "Message": "The request is invalid.",
  "ModelState": {
    "item": [
      "Required property 'Name' not found in JSON. Path '', line 1, position 14."
    ],
    "item.Name": [
      "The Name field is required."
    ],
    "item.Price": [
      "The field Price must be between 0 and 999."
    ]
  }
}

모델 유효성 검사에 대한 자세한 내용은 ASP.NET Web API 모델 유효성 검사를 참조하세요.

HttpResponseException과 함께 HttpError 사용

이전 예제에서는 컨트롤러 작업에서 HttpResponseMessage 메시지를 반환하지만 HttpResponseException 을 사용하여 HttpError를 반환할 수도 있습니다. 이렇게 하면 일반적인 성공 사례에서 강력한 형식의 모델을 반환하고 오류가 있는 경우 HttpError 를 반환할 수 있습니다.

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        throw new HttpResponseException(
            Request.CreateErrorResponse(HttpStatusCode.NotFound, message));
    }
    else
    {
        return item;
    }
}