seangwright
seangwright

Reputation: 18195

Should I return a status code or throw an exception in .Net Web Api 2

I have seen examples like this

public IHttpActionResult GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return Ok(item);
}

But I have also imagine this is an option

public IHttpActionResult GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        return NotFound();
    }
    return Ok(item);
}

Is there an advantage to throwing an exception or simply returning a NotFound (IHttpActionResult instance)?

I know there are stages in the response / request pipeline where either of these results can be handled, like this for the first example

public class NotFoundExceptionFilterAttribute : ExceptionFilterAttribute 
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is NotFoundException)
        {
            // Do some custom stuff here ...
            context.Response = new HttpResponseMessage(HttpStatusCode.NotFound);
        }
    }
}

...

GlobalConfiguration.Configuration.Filters.Add(
    new ProductStore.NotFoundExceptionFilterAttribute());

Upvotes: 6

Views: 5007

Answers (3)

in3xu4
in3xu4

Reputation: 978

A lot depends on your project and what you're trying to achieve. Throwing exceptions for flow control works fine and .NET 9 has gained significant performance improvements. However, exceptions should only be used for exceptional cases. I won't dive into details as you can find many resources online. As a rule of thumb, remember that service methods and external dependencies outside of your controllers shouldn't know anything about HTTP status codes.

Say you have a service method that accepts an ID and returns an object. If the ID is not found, you want to return a NotFound result. You can throw an exception and handle it down the pipeline but consider the drawbacks. First, you're losing performance albeit negligible. Secondly, your flow control is not clear and consequently, documentation, maintenance, and following the code becomes harder. Thirdly and perhaps more importantly, it affects how you write your tests.

Integration tests are always better since you want routing, filters, model binding, authorization, etc. to apply. But unit tests have their place and I think you should do both. Back to our example, if you throw an exception to signify that a resource doesn't exist, your unit tests won't pick that up. How do you write a unit test for testing your controller behavior to identify that a nonexistent ID should return a NotFound result? That's why it's better to use the result object pattern.

In your service method, you do what needs to be done (validation, data access, etc.) and return a result object. In your controller, you map that to appropriate HTTP responses. That gives you better performance, clear intentions, and better maintenance, and you can more easily write tests and document your API.

It's always better to keep your controllers thin, and most applications delegate the work to another handler (i.e. using MediatR). Whether you use the result object pattern or throw exceptions for flow control, you want to ask yourself what is it you're trying to test. And in the context of an API, the answer is the whole pipeline. You want filters, model binding and validation, authorization, and everything else in place, which is why integration tests give you more bang for your buck and make life easier.

More to read:

Upvotes: 0

Claies
Claies

Reputation: 22323

The IHttpActionResult is a feature that was added in WebApi 2. The traditional methods from WebApi 1, i.e. creating an HttpResponseMessage or throwing an HttpResponseException, are still available to you, but the IHttpActionResult was developed to streamline the process.

The IHttpActionResult Interface has one method:

public interface IHttpActionResult
{
    Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken);
}

The NotFound method simply creates a NotFoundResult which returns an empty response with HttpStatusCode.NotFound. This is essentially the exact same thing that throwing HttpResponseException(HttpStatusCode.NotFound) does, but in a more uniform syntax.

The IHttpActionResult interface also allows you to easily create custom ActionResult classes to return any HttpStatusCode or any content type you wish.

Upvotes: 4

AgentFire
AgentFire

Reputation: 9770

Throwing an exception is always should be considered as an exceptional case. While being rare, it still can happen and should be handled properly.

It depends on how often do you need to retrieve nonexistent items from your database. Keep in mind that often exception throwing is always a performance killer.

You should also look into this:

When to throw an exception?

Upvotes: 2

Related Questions