I am returning a NotFound IHttpActionResult
, when something is not found in my WebApi GET action. Along with this response, I want to send a custom message and/
You could use ResponseMessageResult
if you like:
var myCustomMessage = "your custom message which would be sent as a content-negotiated response";
return ResponseMessage(
Request.CreateResponse(
HttpStatusCode.NotFound,
myCustomMessage
)
);
yeah, if you need much shorter versions, then I guess you need to implement your custom action result.
Answers here are missing a little developer story problem. The ApiController
class is still exposing a NotFound()
method that developers may use. This would cause some 404 response to contain a uncontrolled result body.
I present here a few parts of code "better ApiController NotFound method" that will provide a less error-prone method that does not require developers to know "the better way of sending a 404".
ApiController
called ApiController
NotFound
method to let devs use the first available api[Obsolete("Use overload instead")]
protected NotFoundResult NotFound(string message)
that you want to encourageNegotiatedContentResult
. see attached better NotFoundResult class.Here's a one-liner for returning a IHttpActionResult NotFound with a simple message:
return Content(HttpStatusCode.NotFound, "Foo does not exist.");
You'd need to write your own action result if you want to customize the response message shape.
We wanted to provide the most common response message shapes out of the box for things like simple empty 404s, but we also wanted to keep these results as simple as possible; one of the main advantages of using action results is that it makes your action method much easier to unit test. The more properties we put on action results, the more things your unit test needs to consider to make sure the action method is doing what you'd expect.
I often want the ability to provide a custom message as well, so feel free to log a bug for us to consider supporting that action result in a future release: https://aspnetwebstack.codeplex.com/workitem/list/advanced
One nice thing about action results, though, is that you can always write your own fairly easily if you want to do something slightly different. Here's how you might do it in your case (assuming you want the error message in text/plain; if you want JSON, you'd do something slightly different with the content):
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);
}
}
Then, in your action method, you can just do something like this:
public class TestController : ApiController
{
public IHttpActionResult Get()
{
return this.NotFound("These are not the droids you're looking for.");
}
}
If you used a custom controller base class (instead of directly inheriting from ApiController), you could also eliminate the "this." part (which is unfortunately required when calling an extension method):
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.");
}
}
You may use ReasonPhrase property of HttpResponseMessage class
catch (Exception exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
{
ReasonPhrase = exception.Message
});
}
You can create a custom negotiated content result as d3m3t3er suggested. However I would inherit from. Also, if you need it only for returning NotFound, you don't need to initialize the http status from 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);
}
}