Reputation: 18195
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
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
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
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:
Upvotes: 2