如何返回错误消息或exceptionNotFound()IHttpActionResult?

当我的WebApi GET操作中找不到某个东西时,我返回一个NotFound IHttpActionResult 。 随着这个回应,我想发送一个自定义消息和/或exception消息(如果有的话)。 当前ApiControllerNotFound()方法不会提供重传消息。

有没有办法做到这一点? 或者我将不得不写我自己的自定义IHttpActionResult

如果要自定义响应消息形状,则需要编写自己的操作结果。

我们希望提供最简单的响应消息形状,例如简单的空404s,但我们也希望尽可能简单地保留这些结果。 使用动作结果的主要优点之一是它使得你的动作方法更容易进行unit testing。 我们把更多的属性放在行为结果上,unit testing需要考虑的事情越多,以确保行动方法正在做你期望的。

我经常希望能够提供自定义消息,所以请随时logging一个错误,以便我们考虑在未来的版本中支持此操作: https : //aspnetwebstack.codeplex.com/workitem/list/advanced

然而,行动结果的一个好处是,如果你想做一些稍微不同的事情,你总是可以很容易地写出你自己的。 下面是你如何做你的情况(假设你想要的文本/平原的错误信息;如果你想要的JSON,你会做一些与内容略有不同):

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

然后,在你的行动方法中,你可以做这样的事情:

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

如果您使用了自定义控制器基类(而不是直接从ApiControllerinheritance),那么也可以消除“this”。 部分(调用扩展方法时不幸需要):

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

下面是一个简单的消息返回一个IHttpActionResult NotFound的单线程:

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

如果你喜欢,你可以使用ResponseMessageResult

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

是的,如果你需要更短的版本,那么我想你需要实现你的自定义操作结果。

您可以使用HttpResponseMessage类的ReasonPhrase属性

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

我通过简单地从OkNegotiatedContentResult派生出来并在生成的响应消息中覆盖HTTP代码来解决这个问题。 这个类允许你用任何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); } } 

您可以按照d3m3t3er的build议创build一个自定义的协商内容结果。 但是我会inheritance。 另外,如果只需要返回NotFound,就不需要从构造函数初始化http状态。

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

如果您从基础NegotitatedContentResult<T>inheritance,如上所述,并且您不需要转换您的content (例如,您只是想返回一个string),那么您不需要重写ExecuteAsync方法。

所有你需要做的就是提供一个合适的types定义和一个构造函数,告诉基类返回哪个HTTP状态码。 其他一切正常。

这里是NotFoundInternalServerError例子:

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

然后你可以为ApiController创build相应的扩展方法(或者如果你有一个基类的话):

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

然后他们就像内置的方法一样工作。 您可以调用现有的NotFound() ,也可以调用新的自定义NotFound(myErrorMessage)

当然,如果你愿意的话,你可以去掉自定义types定义中的“硬编码”stringtypes,并将其保留为generics,但是你可能不得不担心ExecuteAsync东西,具体取决于你的<T>是。

您可以查看NegotiatedContentResult<T>的源代码以查看它的全部内容。 没有太多的。

我需要在IExceptionHandler类的主体中创build一个IHttpActionResult实例,以便设置ExceptionHandlerContext.Result属性。 不过我也想设置一个自定义的ReasonPhrase

我发现一个ResponseMessageResult可以包装一个HttpResponseMessage (它允许ReasonPhrase很容易设置)。

例如:

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