with tutorial started route parameter net mvc asp asp.net-mvc-4 exception-handling error-handling asp.net-web-api

asp.net mvc 4 - tutorial - Devolver objetos personalizados de error en la API web



web api 2 rest (3)

Creo que esto hará el truco:

Cree una clase de excepción personalizada para la capa empresarial:

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

Cree un método estático para generar un HttpError partir de una instancia de MyException . Estoy usando la reflexión aquí para poder agregar propiedades a MyException y siempre devolverlas sin actualizar 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; }

Actualmente tengo un atributo personalizado para un controlador de excepción general. Todas las excepciones de tipo MyException se manejarán aquí:

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

Voy a jugar con esto un poco más y actualizar ya que encuentro agujeros en este plan.

Tengo una API web en la que estoy trabajando usando el framework MVC 4 Web API. Si hay una excepción, estoy lanzando una nueva HttpResponseException. es decir:

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

Esto devuelve un objeto al cliente que simplemente es {"message":"Invalid id"}

Me gustaría obtener un mayor control sobre esta respuesta a las excepciones devolviendo un objeto más detallado. Algo como

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

¿Cómo voy a hacer esto? ¿Es la mejor manera de serializar mi objeto de error y configurarlo en el mensaje de respuesta?

También he ModelStateDictionary un poco sobre el ModelStateDictionary y he llegado a un poco de "hack", pero todavía no es un resultado claro:

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

editar
parece que HttpError personalizado es lo que necesito. Esto parece ser el truco, ahora para que sea extensible desde mi capa de negocios ...

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



Estas respuestas son mucho más complicadas de lo que necesitan ser.

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

Eso es todo lo que necesitas. También es bueno y fácil de prueba unitaria:

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