Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional parameter in .NET Core Web API route template

[HttpGet("{pageNumber}{pageSize?}{filter?}{sortOrder?}", Name = "GetEntriesPaged")]
public ActionResult<List<Entry>> GetEntriesPaged(
    int pageNumber, int pageSize = 10, string filter = "", string sortOrder = "desc") {

Run-time exception:

System.ArgumentException: An optional parameter must be at the end of the segment. In the segment '{pageNumber}{pageSize?}{filter?}{sortOrder?}', optional parameter 'pageSize' is followed by 'filter'. Parameter name: routeTemplate

What is the point? I have an optional parameter at the end of the segment, as asked....

PS. The more complete code:

[HttpGet]
public ActionResult<List<Entry>> GetAll() {
    var result = _db.Entries.OrderByDescending(x => x.Date).ToList();
    return result;
}

[HttpGet("{pageNumber}{pageSize?}{filter?}{sortOrder?}", Name = "GetEntriesPaged")]
public ActionResult<List<Entry>> GetEntriesPaged(int pageNumber = 1, int pageSize = 10, string filter = "", string sortOrder = "desc") {
    int take = pageSize;
    int skip = ((pageNumber - 1) * pageSize);
    IQueryable<Entry> result;

    if (sortOrder == "asc") {
        result = _db.Entries.OrderBy(x => x.Date);
    }
    else {
        result = _db.Entries.OrderByDescending(x => x.Date);
    }

    return result.Skip(skip).Take(take).Where(x => x.Origin.Contains(filter)).ToList();
}

[HttpGet("{id}", Name = "GetEntry")]
public ActionResult<Entry> GetById(long id) {
    var item = _db.Entries.Find(id);
    if (item == null) {
        return NotFound();
    }
    return item;
}

I need for /entries the GetAll() method to be used, but with /esntries?pageNumber=3 the GetEntriesPaged(...) one

like image 691
serge Avatar asked Sep 18 '18 13:09

serge


1 Answers

No you don't. sortOrder is at the end, but pageSize and filter are not. In short, you cannot have multiple optional parameters like this. It creates too many combinations of routes, which would render it impossible to determine how to route the request or what particular route params to fill. For example, what if you fill in pageSize and sortOrder but not filter? How is ASP.NET Core supposed to know that what you supplied for sortOrder is not actually meant for filter?

FWIW, you also need slashes between these route params. Otherwise, there's no way to know where one ends and the next begins. For example, is a route like /111 saying page one with a size of 11, page 11 with a size of 1, or page 111, and no size set? That still doesn't help you with having them all be optional though.

If you need multiple optional things in the URL, it's best to just use the query string to supply them.

UPDATE

You don't need and really shouldn't have multiple actions for paged or not. There's too much common functionality and the difference is too slight. The typical formula is:

public async Task<IActionResult> GetAll(int? page = null, int pageSize = 10)
{
    var query = _context.Foos;
    if (page.HasValue)
    {
        query = query.Skip((page.Value - 1) * pageSize).Take(pageSize);
    }

    return Ok(await query.ToListAsync());
}
like image 61
Chris Pratt Avatar answered Sep 24 '22 07:09

Chris Pratt