How do I return NotFound() IHttpActionResult with an error message or exception?

105,144

Solution 1

Here's a one-liner for returning a IHttpActionResult NotFound with a simple message:

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

Solution 2

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

Solution 3

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.

Solution 4

You may use ReasonPhrase property of HttpResponseMessage class

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

Solution 5

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);
    }
}
Share:
105,144

Related videos on Youtube

Ajay Jadhav
Author by

Ajay Jadhav

Architecting Decentralized Digital Identity Solutions using Hyperledger Aries, Indy, Ursa Domain-Driven Design practitioner TypeScript, .NET Core Developer, Architect Vue.js, React / React-Native for UI LinkedIn: http://in.linkedin.com/in/jadhavajay

Updated on January 20, 2022

Comments

  • Ajay Jadhav
    Ajay Jadhav over 2 years

    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/or the exception message (if any). The current ApiController's NotFound() method does not provide an overload to pass a message.

    Is there any way of doing this? or I will have to write my own custom IHttpActionResult?

    • Nikolai Samteladze
      Nikolai Samteladze over 10 years
      Do you want to return the same message for all Not Found results?
    • Ajay Jadhav
      Ajay Jadhav over 10 years
      @NikolaiSamteladze No, it could be a different message depending on the situation.
  • Ajay Jadhav
    Ajay Jadhav over 10 years
    Thanks. Well.. this should work, but then I will have to build the HttpResponseException on my own in every action. To keep the code less, I was thinking if I could use any WebApi 2 features (just like the ready-made NotFount(), Ok() methods) and pass the ReasonPhrase message to it.
  • Dmytro Rudenko
    Dmytro Rudenko over 10 years
    You may create your own extension method NotFound(Exception exception), which will throw correct HttpResponseException
  • Kiran
    Kiran over 10 years
    @DmytroRudenko: action results were introduced to improve testability. By throwing HttpResponseException here you would be compromising that. Also here we do not have any exception, but the OP is looking for sending back a message.
  • Dmytro Rudenko
    Dmytro Rudenko over 10 years
    Ok, if you don't want use NUint for testing, you can write your own implementation of NotFoundResult and rewrite its ExecuteAsync for returning your message data. And return instance of this class as a result of your action invocation.
  • Ajay Jadhav
    Ajay Jadhav over 10 years
    I wrote exactly similar implementation of 'IHttpActionResult', but not specific for 'NotFound' result. This will probably work for All 'HttpStatusCodes'. My CustomActionResult code looks something like this And my controler's 'Get()' action looks like this: 'public IHttpActionResult Get() { return CustomNotFoundResult("Meessage to Return."); }' Also, I logged a bug on CodePlex for considering this in the future release.
  • Jerther
    Jerther over 9 years
    I use ODataControllers and I had to use this.NotFound("blah");
  • julealgon
    julealgon about 9 years
    Very nice post, but I'd just like to recommend against the inheritance tip. My team decided to do exactly that a long time ago, and it bloated the classes a lot by doing it. I just recently refactored all of it into extension methods and moved away from the inheritance chain. I'd seriously recommend people to carefully consider when they should use inheritance like this. Usually, composition is a lot better, because it is a lot more decoupled.
  • Jess
    Jess almost 9 years
    People should vote up this answer. It's nice and easy!
  • Theodore Zographos
    Theodore Zographos over 8 years
    This functionality should have been out-of-the-box. Including an optional "ResponseBody" parameter should not affect unit-tests.
  • Mark Sowul
    Mark Sowul over 8 years
    Note that now you can pass the status code directly, e.g. HttpResponseException(HttpStatusCode.NotFound)
  • Andy
    Andy over 8 years
    I went with this method as it seemed neat. I just defined the custom message elsewhere and indented return code.
  • Kasper Halvas Jensen
    Kasper Halvas Jensen about 8 years
    Be aware that this solution do not set the HTTP header status to "404 Not Found".
  • Anthony F
    Anthony F about 8 years
    @KasperHalvasJensen The http status code from the server is 404, do you need something more?
  • Kasper Halvas Jensen
    Kasper Halvas Jensen about 8 years
    @AnthonyF You are right. I was using the Controller.Content(...). Shoud have used the ApiController.Content(...) - My bad.
  • Kaptein Babbalas
    Kaptein Babbalas over 7 years
    Thanks mate, this was exactly what I was looking for
  • pcl
    pcl about 7 years
    Will not work if exception.Message contains e.g. a line break.
  • user1568891
    user1568891 over 6 years
    I like this better than Content because it actually returns a object I can parse with a Message property just like the standard BadRequest method.
  • DiamondDrake
    DiamondDrake over 5 years
    This answer should have more votes, the object passes is still serialized. Works like a charm.