简体   繁体   中英

Generic ActionResult return type for API Controller

I have the following use case for the GetProducts() method.

Good path: Returns array of type product: Product[]

Bad path: Returns status code of 500 and descriptive string error message.

( <?Type?> below is my own markup for the purposes of this post)

[Route("api/[controller]/[action]")]
[ApiController]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public ActionResult<?Type?> GetProducts()
    {
        try
        {
            Product[] products = DataAccess.GetProductsFromDb();
            return products;
        }
        catch 
        {
            Response.StatusCode = 400;
            return "Error retrieving products list"
        }
    }
}

Is there a way I can declare an action result of a generic type or perhaps two types so that this can work?

You would return an IActionResult. I highly recommend making is async as well. Here's how you can return anything through a controller method:

[Route("api/{controller}")]
public class ProductsController : Controller
{
    [HttpGet]
    public async Task<IActionResult> GetProducts()
    {
        var products = await DataAccess.GetProductsFromDb();
        if (products is null)
        {
            return NotFound("Item not found!");
        }
        else
        {
            return Ok(products);
        }
    }
}

Note the Ok and NotFound are methods in the Controller abstract class which allows you to return any object you want, or no object at all.

I highly recommend before continuing using .net core you take a quick look at the example project template in Visual Studio, or if you're developing in another IDE run dotnet new mvc in your terminal.

If you want to handle an exception you should handle it on the lowest level. Assuming GetProductsFromDb() is the lowest level and you have no service layer (you'll regret this design choice later on!) you would put a try/catch.

[Route("api/{controller}")]
public class ProductsController : Controller
{
    [HttpGet]
    public async Task<IActionResult> GetProducts()
    {
        Products[] products;
        try
        {
            products = await DataAccess.GetProductsFromDb();
        }
        catch(Exception e)
        {
            Log.Error(e, "Unable to receive products");
            return InternalServerError("Unable to retrieve products, please try again later");
        }
        if (!products.Any())
        {
            return NotFound("No products were found");
        }
        else
        {
            return Ok(products);
        }
    }
}

Speed is NOT more important than stability in most cases, and certainly not at this stage of development. the cost of catching an exception is insignificant on something so high-level, especially since your database call is going to be several orders of magnitude slower than the overhead of catching a single exception.

You can return ActionResult<Product[]> as you wish. For the error scenario, though, you can use the StatusCode() helper method to return your error message, as follows:

[Route("api/[controller]/[action]")]
[ApiController]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public ActionResult<Product[]> GetProducts()
    {
        try
        {
            Product[] products = DataAccess.GetProductsFromDb();
            return products;
        }
        catch 
        {
            return StatusCode(500, "Error retrieving products list");
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM