ihttpactionresult example c# asp.net-web-api http-status-code-404 httpresponse

c# - example - ¿Cómo devuelvo NotFound() IHttpActionResult con un mensaje de error o una excepción?



ihttpactionresult c# example (8)

Aquí hay una línea para devolver un IHttpActionResult NotFound con un mensaje simple:

return Content(HttpStatusCode.NotFound, "Foo does not exist.");

Estoy devolviendo un NotFound IHttpActionResult , cuando algo no se encuentra en mi acción WebApi GET. Junto con esta respuesta, deseo enviar un mensaje personalizado y / o el mensaje de excepción (si corresponde). El ApiController NotFound() actual de ApiController no proporciona una sobrecarga para pasar un mensaje.

¿Hay alguna forma de hacer esto? o tendré que escribir mi propio IHttpActionResult personalizado?


Lo resolví simplemente derivando de OkNegotiatedContentResult y anulando el código HTTP en el mensaje de respuesta resultante. Esta clase le permite devolver el cuerpo del contenido con cualquier código de respuesta HTTP.

public class CustomNegotiatedContentResult<T> : OkNegotiatedContentResult<T> { public HttpStatusCode HttpStatusCode; public CustomNegotiatedContentResult( HttpStatusCode httpStatusCode, T content, ApiController controller) : base(content, controller) { HttpStatusCode = httpStatusCode; } public override Task<HttpResponseMessage> ExecuteAsync( CancellationToken cancellationToken) { return base.ExecuteAsync(cancellationToken).ContinueWith( task => { // override OK HTTP status code with our own task.Result.StatusCode = HttpStatusCode; return task.Result; }, cancellationToken); } }


Necesitaba crear una instancia IHttpActionResult en el cuerpo de una clase IExceptionHandler , para establecer la propiedad ExceptionHandlerContext.Result . Sin embargo, también quería establecer un ReasonPhrase personalizado.

Descubrí que un ResponseMessageResult podría envolver un HttpResponseMessage (que permite establecer fácilmente ReasonPhrase).

Por ejemplo:

public class MyExceptionHandler : ExceptionHandler { public override void Handle(ExceptionHandlerContext context) { var ex = context.Exception as IRecordNotFoundException; if (ex != null) { context.Result = new ResponseMessageResult(new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = $"{ex.EntityName} not found" }); } } }


Puede crear un resultado de contenido negociado personalizado como se sugirió d3m3t3er. Sin embargo, heredaría de. Además, si lo necesita solo para devolver NotFound, no necesita inicializar el estado de http del constructor.

public class NotFoundNegotiatedContentResult<T> : NegotiatedContentResult<T> { public NotFoundNegotiatedContentResult(T content, ApiController controller) : base(HttpStatusCode.NotFound, content, controller) { } public override Task<HttpResponseMessage> ExecuteAsync( CancellationToken cancellationToken) { return base.ExecuteAsync(cancellationToken).ContinueWith( task => task.Result, cancellationToken); } }


Puede usar ResponseMessageResult si lo desea:

var myCustomMessage = "your custom message which would be sent as a content-negotiated response"; return ResponseMessage( Request.CreateResponse( HttpStatusCode.NotFound, myCustomMessage ) );

Sí, si necesita versiones mucho más cortas, entonces creo que debe implementar su resultado de acción personalizado.


Puede usar la propiedad ReasonPhrase de la clase HttpResponseMessage

catch (Exception exception) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = exception.Message }); }


Si NegotitatedContentResult<T> de la base NegotitatedContentResult<T> , como se mencionó, y no necesitas transformar tu content (por ejemplo, solo quieres devolver una cadena), entonces no necesitas anular el método ExecuteAsync .

Todo lo que necesita hacer es proporcionar una definición de tipo apropiada y un constructor que le indique a la base qué código de estado HTTP debe devolver. Todo lo demás solo funciona

Aquí hay ejemplos para NotFound e InternalServerError :

public class NotFoundNegotiatedContentResult : NegotiatedContentResult<string> { public NotFoundNegotiatedContentResult(string content, ApiController controller) : base(HttpStatusCode.NotFound, content, controller) { } } public class InternalServerErrorNegotiatedContentResult : NegotiatedContentResult<string> { public InternalServerErrorNegotiatedContentResult(string content, ApiController controller) : base(HttpStatusCode.InternalServerError, content, controller) { } }

Y luego puede crear los métodos de extensión correspondientes para ApiController (o hacerlo en una clase base si tiene uno):

public static NotFoundNegotiatedContentResult NotFound(this ApiController controller, string message) { return new NotFoundNegotiatedContentResult(message, controller); } public static InternalServerErrorNegotiatedContentResult InternalServerError(this ApiController controller, string message) { return new InternalServerErrorNegotiatedContentResult(message, controller); }

Y luego funcionan igual que los métodos integrados. Puede llamar a NotFound() existente o puede llamar a su nuevo NotFound(myErrorMessage) personalizado NotFound(myErrorMessage) .

Y por supuesto, puede deshacerse de los tipos de cadenas "codificadas" en las definiciones de tipos personalizados y dejarlas genéricas si lo desea, pero puede que tenga que preocuparse por las cosas ExecuteAsync , dependiendo de lo que su <T> realidad es.

Puede consultar el código fuente de NegotiatedContentResult<T> para ver todo lo que hace. No hay mucho para eso.


Tendrá que escribir su propio resultado de acción si desea personalizar la forma del mensaje de respuesta.

Queríamos proporcionar las formas más comunes de mensajes de respuesta para cosas como simples 404 vacíos, pero también queríamos mantener estos resultados lo más simple posible; Una de las principales ventajas del uso de los resultados de acción es que hace que su método de acción sea mucho más fácil para la prueba unitaria. Cuantas más propiedades ponemos en los resultados de acción, más cosas debe considerar su prueba de unidad para asegurarse de que el método de acción está haciendo lo que usted esperaría.

A menudo también quiero la posibilidad de proporcionar un mensaje personalizado, así que siéntase libre de registrar un error para que nosotros consideremos apoyar el resultado de esa acción en una versión futura: https://aspnetwebstack.codeplex.com/workitem/list/advanced

Una cosa buena de los resultados de acción, sin embargo, es que siempre puedes escribir la tuya con bastante facilidad si quieres hacer algo ligeramente diferente. Así es como puede hacerlo en su caso (suponiendo que desea el mensaje de error en texto / simple, si desea JSON, haría algo ligeramente diferente con el contenido):

public class NotFoundTextPlainActionResult : IHttpActionResult { public NotFoundTextPlainActionResult(string message, HttpRequestMessage request) { if (message == null) { throw new ArgumentNullException("message"); } if (request == null) { throw new ArgumentNullException("request"); } Message = message; Request = request; } public string Message { get; private set; } public HttpRequestMessage Request { get; private set; } public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { return Task.FromResult(Execute()); } public HttpResponseMessage Execute() { HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.NotFound); response.Content = new StringContent(Message); // Put the message in the response body (text/plain content). response.RequestMessage = Request; return response; } } public static class ApiControllerExtensions { public static NotFoundTextPlainActionResult NotFound(this ApiController controller, string message) { return new NotFoundTextPlainActionResult(message, controller.Request); } }

Luego, en su método de acción, puede hacer algo como esto:

public class TestController : ApiController { public IHttpActionResult Get() { return this.NotFound("These are not the droids you''re looking for."); } }

Si utilizó una clase base de controlador personalizado (en lugar de heredar directamente de ApiController), también podría eliminar el "esto". parte (que lamentablemente se requiere al llamar a un método de extensión):

public class CustomApiController : ApiController { protected NotFoundTextPlainActionResult NotFound(string message) { return new NotFoundTextPlainActionResult(message, Request); } } public class TestController : CustomApiController { public IHttpActionResult Get() { return NotFound("These are not the droids you''re looking for."); } }