5

I have an action like this:

[HttpGet]
[Route("~/books/{id:int:min(1)}/{slug?}")]
public ActionResult Book(int? id, string slug)
{
    if (slug == null)
    {
        slug = "awesome-book";
        return RedirectToAction("Book", new { id, slug });
    }

    etc.
}

The problem is that the new route is generated like 'books/1?slug=awesome-book' and that's not what I wanted but 'books/1/awesome-book'. How can I properly set the slug?

Drew Gaynor
  • 8,292
  • 5
  • 40
  • 53
Dan
  • 1,555
  • 2
  • 14
  • 30

1 Answers1

4

This is a problem with generating route URLs. Since the slug route param is optional, the routing framework stops at /books/1 and then tacks on any params not covered by the URL as a query string, which in this case includes slug. This is due to the short circuiting system the routing framework employs and there's really nothing you can do about it.

There is a workaround, though. If instead of using an optional param, you use another route, you can name that route and then reference it explicitly. For example:

[Route("~/books/{id:int:min(1)}", Order = 1)]
[Route("~/books/{id:int:min(1)}/{slug}", Order = 2, Name = "BookWithSlug")]

Then, you can generate the URL with:

return RedirectToRoute("BookWithSlug", new { id, slug });

And, you'll end up with the URL you want.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Yes, thank you, this does the job unfortunately if you have another route like "/books/1/pages" it will detect multiple routes because it cannot distinguish between a slug and the last segment from another route. – Dan Dec 17 '14 at 20:20
  • 1
    Technically, that was a problem even before, as if you tried to go to `/books/1/pages` you'd have two matching matching routes as well. Nevertheless, you can simply change your route param to `{slug:regex(^(?!pages)$)}` – Chris Pratt Dec 17 '14 at 20:24
  • Agree and thank you for the regex solution, yet it doesn't feel ok because if I have a book called "pages" I will get into trouble. I think I'll append a slug only to routes where I know will not have "subroutes" unless maybe you have a better idea (and have the id as well)? I'm thinking about chaging the "subroutes" to /books/pages?bookId=1 although it's not that pretty. – Dan Dec 17 '14 at 20:29
  • 1
    Well, an alternative would be to forgo having both an id and a slug and just use one or the other. Then the ambiguity is removed as you'll either have `/books/1/pages` or `/books/some-awesome-book/pages`. Personally, I'd say just drop the id and use the slug to query your objects. – Chris Pratt Dec 17 '14 at 20:36