Phillip Davis
Phillip Davis

Reputation: 325

MVC5 Rewriting Routing Attribute - Default page

I am attempting to switch from RouteConfig to Routing Attributes.

I am following along the Pro ASP.NET MVC 5 book from Adam Freeman and I'm trying to convert the following code that handles the paging of clients.

 routes.MapRoute(
      name: null,
      url: "{controller}/Page{page}",
      defaults: new { action = "Index", status = (string)null },
      constraints: new { page = @"\d+" }
  );

This works great! As I go to different URLs, the links look very nice

 http://localhost:65534/Client - Default page
 http://localhost:65534/Client/Page2 - Second page

Now I've decided to try out Url Attributes and having a bit of problems when it comes to how 'pretty' the links are. All of the links are working fine, but it's the 'routing rewriting' that I am trying to fix.

Here are the important parts of my controller.

[RoutePrefix("Client")]
[Route("{action=index}/{id:int?}")]

public class ClientController : Controller {

    [Route("Page{page:int?}")]

    public ActionResult Index(string sortOrder, string search = null, int page = 1) {

With the attribute above the Index, going to /Client or to /Client/Page gives me a 404.

Adding a blank route to catch the default page

    [Route("Page{page:int?}")]
    [Route]

Works for /Client and /Client/Page3, but now the rewriting of the URL is messed up. Clicking on page 3 of the pager gives me a URL of

    http://localhost:65534/Client?page=3

which is not what I want. Changing the routing to

    [Route("Page{page:int?}")]
    [Route("{page=1:int?}")]

Works almost 100%, but the default link for /Client is now

    http://localhost:65534/Client/Page

So, I am now asking for help. How can I correctly convert the original MapRoute to the attributes?

Upvotes: 0

Views: 757

Answers (1)

Chris Pratt
Chris Pratt

Reputation: 239430

Just use:

[Route("", Order = 1)]
[Route("Page{page:int}", Order = 2)]

UPDATE

Plainly and simply, the routing framework is dumb. It doesn't make decisions about which route is the most appropriate, it merely finds a matching route and returns. If you do something like:

Url.Action("Index", "Client", new { page = 1 })

You're expecting the generated URL to be /Client/Page1, but since you have a route where page is essentially optional, it always will choose that route and append anything it can't stuff into the URL as a querystring, i.e. /Client?page=1. The only way to get around this is to actually name the route you want and use that named route to generate the URL. For example:

[Route("", Order = 1)]
[Route("Page{page:int}", Name = "ClientWithPage", Order = 2)]

And then:

Url.RouteUrl("ClientWithPage", new { page = 1 })

Then, you'll get the route you expect because you're directly referencing it.

UPDATE #2

I'm not sure what you mean by "go into PagedList.MVC and add a name property to it". It doesn't require any core changes to the code because PagedList already has support for custom page links. Just change your pager code to something like:

@Html.PagedListPager((IPagedList)ViewBag.OnePageOfItems, page => Url.RouteUrl("ClientWithPage", new { page = page }))

And you'll get the URL style you want. Attribute routing can be a bit more finicky than traditional routing, but I'd hardly call it useless. It's far more flexible than traditional routing, but that flexibility has some costs.

Upvotes: 2

Related Questions