30

Tôi có API web Tôi đang làm việc với việc sử dụng khuôn khổ API MVC 4 Web API. Nếu có một ngoại lệ, tôi hiện đang ném một HttpResponseException mới. ví dụ:Trả về các đối tượng lỗi tùy chỉnh trong API Web

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

này trả về một đối tượng cho khách hàng mà chỉ đơn giản là {"message":"Invalid id"}

Tôi muốn giành quyền kiểm soát hơn nữa trong phản ứng này ngoại lệ bằng cách quay một đối tượng chi tiết hơn. Một cái gì đó như

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

Tôi sẽ làm gì? Cách tốt nhất để sắp xếp lại đối tượng lỗi của tôi và đặt nó trong thông báo phản hồi?

Tôi cũng đã nhìn vào ModelStateDictionary một chút và đã đưa ra chút này của một "hack", nhưng nó vẫn không phải là một kết quả sạch:

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

chỉnh sửa
trông giống như một tuỳ chỉnh HttpError là những gì tôi cần. Điều này dường như làm các trick, bây giờ để làm cho nó có thể mở rộng từ lớp doanh nghiệp của tôi ...

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

Trả lời

9

Tôi nghĩ rằng điều này sẽ làm các trick:

Tạo một lớp ngoại lệ tùy chỉnh cho các lớp kinh doanh:

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

Tạo phương pháp tĩnh để tạo HttpError từ phiên bản MyException. Tôi đang sử dụng phản ánh ở đây để tôi có thể thêm các thuộc tính để MyException và luôn luôn đã cho họ trở lại w/o cập nhật Create:

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

Tôi hiện đang có một thuộc tính tùy chỉnh cho một handler ngoại lệ chung. Tất cả các trường hợp ngoại lệ của loại MyException sẽ được xử lý ở đây:

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

tôi sẽ chơi xung quanh với điều này hơn một chút và cập nhật như tôi tìm ra nhược điểm trong kế hoạch này.

+3

Tại sao bạn ẩn thuộc tính Message? Nó sẽ không an toàn hơn để gọi ctor cơ sở và truyền thông điệp theo cách đó? – Andy

2

Hãy xem bài viết sau. Điều này sẽ giúp bạn giành quyền kiểm soát các ngoại lệ và thông báo lỗi trên web api của mình: Web Api, HttpError, and the Behavior of Exceptions

+1

Cảm ơn. Điều đó tương tự như những gì tôi đang làm - tạo một ExceptionFilterAttribute tuỳ chỉnh – earthling

+3

Trang web đó không còn có sẵn – TravisO

39

Những câu trả lời này phức tạp hơn mức cần thiết.

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

Đó là tất cả những gì bạn cần. Nó cũng tốt đẹp và dễ dàng để kiểm tra đơn vị:

[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

Câu trả lời hay, +1 cho cả việc kiểm tra thích hợp – xingyu

+0

Đảm bảo bạn đã sử dụng System.Net.Http; – Sal

+0

Đây là câu trả lời hay nhất. Mạnh mẽ hơn và dễ thực hiện hơn.Trong biến thể của tôi, trong đối tượng ẩn danh, tôi đã thêm một số thuộc tính với thông báo ngoại lệ và các gợi ý hữu ích khác để gỡ lỗi. Cảm ơn! –

Các vấn đề liên quan