30

웹 API가 있습니다. MVC 4 웹 API 프레임 워크를 사용하고 있습니다. 예외가있는 경우 현재 새 HttpResponseException을 던지고 있습니다. 예 :웹 API에서 사용자 정의 오류 객체 반환

if (!Int32.TryParse(id, out userId)) 
    throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid id")); 

이 단순히 {"message":"Invalid id"}

내가 더 상세한 객체를 반환하여 예외이 응답을 통해 더 관리 권한을 획득하고자하는 클라이언트에 개체를 반환합니다. 예 :

{ 
"status":-1, 
"substatus":3, 
"message":"Could not find user" 
} 

어떻게하면됩니까? 내 오류 개체를 serialize하고 응답 메시지에서 설정하는 가장 좋은 방법은 무엇입니까?

나는 또한 ModelStateDictionary에 비트를 검토 한 결과는 "해킹"의 비트와 함께 온,하지만 여전히 깨끗한 출력이 아니다 :

var msd = new ModelStateDictionary(); 
msd.AddModelError("status", "-1"); 
msd.AddModelError("substatus", "3"); 
msd.AddModelError("message", "invalid stuff"); 
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, msd)); 

편집
이처럼 보인다 맞춤 HttpError이 필요합니다.

비즈니스 계층에 대한 사용자 정의 예외 클래스를 만듭니다 :

var error = new HttpError("invalid stuff") {{"status", -1}, {"substatus", 3}}; 
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, error)); 

답변

9

나는이 트릭을 할 것입니다 생각 ... 내 비즈니스 계층에서 그것을 확장 할 지금, 트릭을 할 것 같다
public class MyException: Exception 
{ 
    public ResponseStatus Status { get; private set; } 
    public ResponseSubStatus SubStatus { get; private set; } 
    public new string Message { get; private set; } 

    public MyException() 
    {} 

    public MyException(ResponseStatus status, ResponseSubStatus subStatus, string message) 
    { 
     Status = status; 
     SubStatus = subStatus; 
     Message = message; 
    } 
} 

MyException 인스턴스에서 HttpError을 생성하는 정적 메서드를 만듭니다.

public static HttpError Create<T>(MyException exception) where T:Exception 
    { 
     var properties = exception.GetType().GetProperties(BindingFlags.Instance 
                 | BindingFlags.Public 
                 | BindingFlags.DeclaredOnly); 
     var error = new HttpError(); 
     foreach (var propertyInfo in properties) 
     { 
      error.Add(propertyInfo.Name, propertyInfo.GetValue(exception, null)); 
     } 
     return error; 
    } 

나는 현재 일반적인 예외 핸들러에 대한 사용자 정의 속성이 : 나는 Create를 업데이트하여 w/o 그래서 MyException에 속성을 추가 할 수 있으며 항상 돌아왔다 여기 반사를 사용하고 있습니다. 유형 MyException의 모든 예외는 여기에 처리됩니다 :이 계획에 구멍을 발견하면

public class ExceptionHandlingAttribute : ExceptionFilterAttribute 
{ 
    public override void OnException(HttpActionExecutedContext context) 
    { 
     var statusCode = HttpStatusCode.InternalServerError; 

     if (context.Exception is MyException) 
     { 
      statusCode = HttpStatusCode.BadRequest; 
      throw new HttpResponseException(context.Request.CreateErrorResponse(statusCode, HttpErrorHelper.Create(context.Exception))); 
     } 

     if (context.Exception is AuthenticationException) 
      statusCode = HttpStatusCode.Forbidden; 

     throw new HttpResponseException(context.Request.CreateErrorResponse(statusCode, context.Exception.Message)); 
    } 
} 

나는이 좀 더 업데이트와 함께 놀러 것이다.

+3

왜 Message 속성을 숨기고 있습니까? 기본 ctor를 호출하고 메시지를 그렇게 전달하는 것이 더 안전하지 않습니까? – Andy

2

다음 기사를 살펴보십시오. 웹 API 예외 및 오류 메시지를 제어 할 수 있습니다. Web Api, HttpError, and the Behavior of Exceptions

+1

감사합니다. 그건 내가하고있는 일과 비슷하다. 커스텀 ExceptionFilterAttribute를 만든다. – earthling

+3

그 사이트는 더 이상 쓸모가 없다. – TravisO

39

이러한 대답은 필요한 것보다 훨씬 복잡합니다.

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     config.Filters.Add(new HandleApiExceptionAttribute()); 
     // ... 
    } 
} 

public class HandleApiExceptionAttribute : ExceptionFilterAttribute 
{ 
    public override void OnException(HttpActionExecutedContext context) 
    { 
     var request = context.ActionContext.Request; 

     var response = new 
     { 
      //Properties go here... 
     }; 

     context.Response = request.CreateResponse(HttpStatusCode.BadRequest, response); 
    } 
} 

그게 전부입니다. 단위 테스트에도 유용합니다.

[Test] 
public async void OnException_ShouldBuildProperErrorResponse() 
{ 
    var expected = new 
    { 
     //Properties go here... 
    }; 

    //Setup 
    var target = new HandleApiExceptionAttribute() 

    var contextMock = BuildContextMock(); 

    //Act 
    target.OnException(contextMock); 

    dynamic actual = await contextMock.Response.Content.ReadAsAsync<ExpandoObject>(); 

    Assert.AreEqual(expected.Aproperty, actual.Aproperty); 
} 

private HttpActionExecutedContext BuildContextMock() 
{ 
    var requestMock = new HttpRequestMessage(); 
    requestMock.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration()); 

    return new HttpActionExecutedContext() 
    { 
     ActionContext = new HttpActionContext 
     { 
      ControllerContext = new HttpControllerContext 
      { 
       Request = requestMock 
      } 

     }, 
     Exception = new Exception() 
    }; 
} 
+0

우수 답변, 적절한 테스트를 포함하여 +1 또한 – xingyu

+0

System.Net.Http; – Sal

+0

이것은 최고의 대답입니다. 보다 강력하고 구현하기 쉽습니다.유사하게, 익명 객체에서 예외 메시지 및 디버깅에 유용한 다른 힌트를 추가했습니다. 감사! –

관련 문제