36

In ASP.NET Web API 2, what is the difference among the following?

public async Task<IEnumerable<MyItem>> GetMyItems()
{
    //... code ..., var myItems = await ...
    return myItems;
}

and

public async Task<IQueryable<MyItem>> GetMyItems()
{
    //... code ..., var myItems = await ...
    return myItems;
}

and

public async Task<IHttpActionResult> GetMyItems()
{
    //... code ..., var myItems = await ...
    return Ok(myItems);
}

Should I return IHttpActionResult or IEnumerable<MyItem> / IQueryable<MyItem> ?

DavidRR
  • 18,291
  • 25
  • 109
  • 191
Adam Szabo
  • 11,302
  • 18
  • 64
  • 100
  • If your action method does not include the use of the `await` keyword, then you should not return a `Task`. Instead, you should simply return `IHttpActionResult`, `IEnumerable` or `IQueryable`. See the question [IHttpActionResult vs async Task](https://stackoverflow.com/q/29100732/1497596). Also see the question [Effectively use async/await with ASP.NET Web API](https://stackoverflow.com/q/31185072/1497596). – DavidRR Jan 12 '18 at 18:40

3 Answers3

31

You should return IHttpActionResult because you can be more specific to the client. You can create more user friendly web applications. Basically you can return different HTML status messages for different situations.

For example:

public async Task<IHttpActionResult> GetMyItems()
{
    if(!authorized)
        return Unauthorized();
    if(myItems.Count == 0)
        return NotFound();
    //... code ..., var myItems = await ...
    return Ok(myItems);
}

IEnumerableand IQueryable will just parse your data into the output format and you will not have a proper way to handle exceptions. That is the most important difference.

turhanco
  • 941
  • 10
  • 19
  • Given that `await` is not used within `GetMyItems()`, its signature should simply be `public IHttpActionResult GetMyItems()`. From the question [IHttpActionResult vs async Task](https://stackoverflow.com/q/29100732/1497596), see [this answer](https://stackoverflow.com/a/38699810/1497596) which states: "If your controller action code doesn't use `await` then you can switch back to the simpler signature. However, the result you return will still be asynchronous." – DavidRR Jan 12 '18 at 18:33
11

I would choose between IEnumerable and IHttpActionResult, you can do pretty much the same thing with both of these just in slightly different ways. IQueryable is usually used for lower level data access tasks and deferred execution of sql queries so I'd keep it encapsulated within your data access classes and not expose it with the web api.

Here's a summary from http://www.asp.net/web-api/overview/web-api-routing-and-actions/action-results:

IHttpActionResult

The IHttpActionResult interface was introducted in Web API 2. Essentially, it defines an HttpResponseMessage factory. Here are some advantages of using the IHttpActionResult interface (over the HttpResponseMessage class):

  • Simplifies unit testing your controllers.
  • Moves common logic for creating HTTP responses into separate classes.
  • Makes the intent of the controller action clearer, by hiding the low-level details of constructing the response.

IEnumerable<Item>

For all other return types, Web API uses a media formatter to serialize the return value. Web API writes the serialized value into the response body. The response status code is 200 (OK).

public class ProductsController : ApiController
{
    public IEnumerable<Product> Get()
    {
        return GetAllProductsFromDB();
    }
}

A disadvantage of this approach is that you cannot directly return an error code, such as 404. However, you can throw an HttpResponseException for error codes. For more information.

Jason Watmore
  • 4,521
  • 2
  • 32
  • 36
  • 2
    -1 "IQueryable is usually used for lower level data access tasks and deferred execution of sql queries so I'd keep it encapsulated within your data access classes and not expose it with the web api." This is completely wrong. Without exposing IQueryable, it is difficult to use `OData` query urls. http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/create-an-odata-v4-endpoint – Aron Aug 19 '14 at 01:20
  • 2
    @Aron I agree this doesn't apply to OData, but a lot of people would disagree with you about exposing IQueryable outside of the repository layer: http://codetunnel.com/should-you-return-iqueryablet-from-your-repositories/, http://programmers.stackexchange.com/questions/192044/should-repositories-return-iqueryable, http://mikehadlow.blogspot.com.au/2009/01/should-my-repository-expose-iqueryable.html. And some people even describe OData itself as an anti-pattern https://github.com/ServiceStack/ServiceStack/wiki/Auto-Query#why-not-odata – Jason Watmore Aug 19 '14 at 04:52
  • Those are the same people i would argue that inner platform is an anti pattern. – Aron Aug 19 '14 at 05:38
  • 1
    +1 I am looking to implement OData urls and your answer lead me down the right path for error handling. HttpResponseException is what I needed. Thanks! – Jason Nov 04 '14 at 16:18
  • 1
    I would not use IEnumerable if you have an exception filter https://www.neovolve.com/2013/02/20/beware-of-returning-ienumerable-in-a-web-api-action/ – Conner Apr 29 '19 at 20:12
  • 2
    I am here to say that the "...throw an HttpResponseException ..." is what I needed. Thanks @Jason – AceMark Sep 03 '20 at 16:27
0

If you got here, you are probably looking for how to return an error status code instead of actual data in an API which returns an IEumerable.

The answer is, throw an HttpResponseException. Then there is no need to use an ActionResult which clutters up otherwise clean code.

Here is a full example from the Docs:

var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
    {
        Content = new StringContent(string.Format("No product with ID = {0}", id)),
        ReasonPhrase = "Product ID Not Found"
    };
    throw new HttpResponseException(resp);

The credit for this answer goes to @JasonWatmore above. This is just to make this tip more clear as it is quite useful.

Greg Gum
  • 33,478
  • 39
  • 162
  • 233