login register Sysop! about ME  
qrcode
    최초 작성일 :    2013년 10월 02일
  최종 수정일 :    2013년 10월 02일
  작성자 :    songgun
  편집자 :    songgun (송 원석)
  읽음수 :    13,811

강좌 목록으로 돌아가기

필자의 잡담~

Web API 관련 여섯번째 번역 문서입니다.
예외를 처리하는 다양한 방법에 대한 이야기입니다. ㅎㅎ

본 문서에서는 ASP.NET Web API의 오류 및 예외 처리에 관해서 살펴봅니다.

HttpResponseException

Web API의 컨트롤러에서 처리되지 않은 예외가 발생하면 어떤 일이 발생할까요? 기본적으로 대다수의 예외는 내부 서버 오류를 뜻하는 상태 코드 500 HTTP 응답으로 변환됩니다.

그러나, HttpResponseException 형식은 별개로 취급됩니다. 이 예외 형식은 생성자를 통해서 지정한 HTTP 상태 코드를 반환합니다. 가령, 다음 메서드는 id 매개변수가 유효하지 않은 경우, HTTP 상태 코드 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;
}

예외 필터

또 다른 방법으로 예외 필터 (Exception Filter)를 작성해서 Web API가 예외를 처리하는 방식을 직접 지정할 수도 있습니다. 예외 필터는 컨트롤러 메서드에서 HttpResponseException제외한 모든 종류의 처리되지 않은 예외가 던져지면 실행됩니다. 이 HttpResponseException 형식은 특별하게 취급되는데, 그 이유는 애초에 HTTP 응답 반환을 위해 설계된 예외이기 때문입니다.

예외 필터는 System.Web.Http.Filters.IExceptionFilter 인터페이스를 구현해야 합니다. 예외 필터를 구현하는 가장 간단한 방법은 System.Web.Http.Filters.ExceptionFilterAttribute 클래스를 상속 받아서 OnException 메서드를 재정의 하는 것입니다.

ASP.NET Web API의 예외 필터와 ASP.NET MVC의 예외 필터는 매우 비슷합니다. 그러나, 이 두 가지는 서로 다른 네임스페이스에 선언되어 있으며 기능 자체도 다릅니다. 예를 들어서, MVC의 예외 필터에 사용되는 HandleErrorAttribute 클래스는 Web API 컨트롤러에서 던져지는 예외를 처리하지 않습니다.

다음은 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 예외 필터를 등록할 수 있는 방법은 다음과 같습니다:

  1. 액션에 등록
  2. 컨트롤러에 등록
  3. 전역 등록

특정 액션에 예외 필터를 적용하려면, 필터를 액션에 어트리뷰트로 추가합니다:

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 컨트롤러에 전역으로 필터를 적용하려면, GlobalConfiguration.Configuration.Filters 컬렉션에 예외 필터의 인스턴스를 추가합니다. 이 컬렉션에 존재하는 예외 필터들은 모든 Web API 컨트롤러 액션에 적용됩니다.

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

만약, 프로젝트를 "ASP.NET MVC 4 웹 응용 프로그램" 프로젝트 템플릿을 통해서 생성했다면, 다음과 같이 App_Start 폴더에 위치해 있는 WebApiConfig 클래스에 Web API 구성 코드를 추가하면 됩니다:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
    config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute());
    
    // 다른 구성 코드들...
    }
}

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);
    HttpError err = new HttpError(message);
    return Request.CreateResponse(HttpStatusCode.NotFound, err);
    }
    else
    {
    return Request.CreateResponse(HttpStatusCode.OK, item);
    }
}

이 예제의 메서드는 조회에 성공한 경우, 제품 자체를 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를 사용할 때 얻을 수 있는 이점 중 하나가 바로 이것으로, 다른 모든 강력한 형식의 모델과 동일하게 내용 협상 (Content-Negotiation) 및 직렬화 처리가 수행된다는 점입니다.

HttpError 개체를 직접 생성하는 대신, CreateErrorResponse 메서드를 사용할 수도 있습니다:

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);
    }
}

CreateErrorResponse 메서드는 System.Net.Http.HttpRequestMessageExtensions 클래스에 정의되어 있는 확장 메서드입니다. 내부적으로, CreateErrorResponse 메서드는 HttpError의 인스턴스를 생성한 다음, 그 HttpError 개체를 포함하는 HttpResponseMessage 개체를 생성합니다.

HttpError 및 모델 유효성 검사

모델의 유효성 검사를 목적으로, 모델의 상태를 CreateErrorResponse에 전달해서 유효성 검사의 오류 내용들을 응답에 포함시킬 수도 있습니다:

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

이 예제는 다음과 같은 응답 형태를 갖게 될 것입니다:

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."
    ]
    }
}

모델 유효성 검사에 대한 보다 자세한 정보는 Model Validation in ASP.NET Web API를 참고하시기 바랍니다.

HttpError에 사용자 정의 키-값 쌍 추가하기

지금까지 살펴본 HttpError 클래스는 키-값 구조를 가진 컬렉션(Dictionary<string, object>에서 파생된)일 뿐입니다. 그러므로, 얼마든지 자신이 원하는 키-값 쌍을 추가할 수 있습니다:

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

HttpError와 HttpResponseException을 동시에 사용하기

직전 예제는 액션에서 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;
    }
}

authored by

  itist
  2013-10-08(10:54)
캐릭 이미지
감사합니다~

 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})