Search code examples
c#angularasp.net-corerazor-pagesasp.net-core-mvc-2.1

Pagination and Pages conflict - ASP.Net Core Razor Pages conflicts with MVC controller method pagination


In an ASP.Net Core 2.1 with Angular 6 spa project, I return a paginated list of users using this AccountController.cs method:

[HttpGet("users")]
[Produces(typeof(List<UserViewModel>))]
public async Task<IActionResult> GetUsers()
{
    return await GetUsers(-1, -1);
}

[HttpGet("users/{page:int}/{pageSize:int}")]             <---- conflicts here
[Produces(typeof(List<UserViewModel>))]
public async Task<IActionResult> GetUsers(int page, int pageSize)
{
    var users = await _accountManager.GetUsersLoadRelatedAsync(page, pageSize);
    return Ok(Mapper.Map<List<UserViewModel>>(users));
}

In startup.cs I am using MVC as follows:

app.UseMvc(routes =>
{
    routes.MapRoute("blog", "blog/{*article}", defaults: new { controller = "Blog", action = "Article" });
    routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});

Code as described above works fine, but as I want to start using Razor Pages in my project, I created a new standard Razor Pages project template and added its Pages folder to the current project, but as soon as I do that and run the project I get error, if I move out again the Pages folder, the project start running without errors.

Here is the error description:

RouteCreationException: The following errors occurred with attribute routing information:

For action: 'MyApiProject.Controllers.AccountController.GetUsers (MyApiProject)'

Error: The attribute route 'api/Account/users/{page:int}/{pageSize:int}' cannot contain a parameter named '{page}'. Use '[page]' in the route template to insert the value ''.

The Method code hasn't to be changed since it is working just fine with the existing Angular spa. So what's the workaround ?


Solution

  • When using ASP.NET Core MVC Razor Pages, {page} becomes what is effectively a reserved word, just as {controller} and {action} are when working with traditional MVC controllers and actions. In order to work around this, you can simply use something else to represent the page number desired when performing pagination. For example:

    [HttpGet("users/{pageNumber:int}/{pageSize:int}")]
    [Produces(typeof(List<UserViewModel>))]
    public async Task<IActionResult> GetUsers(int pageNumber, int pageSize)
    {
        var users = await _accountManager.GetUsersLoadRelatedAsync(pageNumber, pageSize);
        return Ok(Mapper.Map<List<UserViewModel>>(users));
    }
    

    Because you were previously using {page} as a route-value, switching this to {pageNumber} will have no affect on calling applications as they did not use page directly. Had this been a query-string parameter, that would be a different story.