如何返回错误消息或exceptionNotFound()IHttpActionResult?
当我的WebApi GET操作中找不到某个东西时,我返回一个NotFound IHttpActionResult
。 随着这个回应,我想发送一个自定义消息和/或exception消息(如果有的话)。 当前ApiController
的NotFound()
方法不会提供重传消息。
有没有办法做到这一点? 或者我将不得不写我自己的自定义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状态码。 其他一切正常。
这里是NotFound
和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) { } }
然后你可以为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" }); } } }