0

I have the following route table:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "UserRoute",
            url: "{username}",
            defaults: new { controller = "User", action = "Index" }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }

so, when we have url like : http://mysite/abcde it calls UserController, action Index, if we have url like : http://mysite/dashboard/Index it calls DashboardController, action Index. Ok. But when I try to call the following:

        return RedirectToAction("Index", "Dashboard");

It calls UserController, action Index with username parameter equals "Dashboard". Why and how to solve?

Oleg Sh
  • 8,496
  • 17
  • 89
  • 159
  • You `UserRoute` matches anything with only one segment (which your `RedirectToAction()` generates because the default action is `Index`) –  Mar 20 '17 at 21:10
  • @StephenMuecke is right. What are you trying to achieve with your `userRoute`?. By default all controllers redirect to the index action if no action is passed... correct? – David Espino Mar 20 '17 at 21:15
  • It sounds to me like you need to create a custom route constraint that only matches the `{username}` section of your first route if, and only if a user exists of that name. Even then, you'd still be in trouble if a user decided to call themselves `Home`... The core of this problem is having such a greedy route for `UserRoute`. Perhaps `User/{username}` might be a more workable route that doesn't tread on the toes of your second route? – spender Mar 20 '17 at 21:20
  • I should have definitely http://mysite/abcde, not http://mysite/User/abcde. How to say ASP.NET MVC to generate a path "/Dashboard/Index" when I call return RedirectToAction("Index", "Dashboard"); ? – Oleg Sh Mar 20 '17 at 21:23
  • @OlegSh, You cannot, unless you create a route constraint for the `UserRoute` (that looks up a repository to determine if the value of `{username}` matches, but that's not very efficient and will fail if a `username` also matches a controller name –  Mar 20 '17 at 21:25
  • If you really do want to use that route, then refer [this answer](http://stackoverflow.com/questions/37358416/routing-in-asp-net-mvc-showing-username-in-url/37359345#37359345) for an example. –  Mar 20 '17 at 21:26
  • 1
    @OlegSh Just think about what you are asking for. This means that anybody writing a new controller will need to know which users exist, and users will not be allowed to have usernames that match controller names. That sucks. Think really hard about this. It will be a pain. It will be pain. – spender Mar 20 '17 at 21:30
  • why controller should know about users? If path has controller and action (i.e. /Dashboard/Index)- then we call that controller and action, if not - then call UserController, action Index. It seems logically... – Oleg Sh Mar 20 '17 at 21:41
  • @OlegSh, Your url does NOT have a controller and action segment. `RedirectToAction("Index", "Dashboard");` generates `.../Dashboard`, not `../Dashboard/Index` (as does `@Url.Action("Index", "Dashboard")`) because `Index` is the default action. Therefore it matches the `UserRoute` and calls the `Index()` method of `UserController` –  Mar 20 '17 at 21:45
  • @OlegSh Ok. So remove the default `action = "Index"` from your second route. Now any route that isn't `/username` will *always* require at least 2 segments, and `RedirectToAction("Index", "Dashboard");` will generate `/Dashboard/Index` – spender Mar 20 '17 at 21:49
  • @StephenMuecke I understand, why it calls, I don't understand, why no way to generate /Dashboard/Index instead /Dashboard :) – Oleg Sh Mar 20 '17 at 23:18
  • @spender "The RouteData must contain an item named 'action' with a non-empty string value." – Oleg Sh Mar 20 '17 at 23:22
  • @OlegSh, Because that's the way it works. There are all sorts of hacks you could use such as `RedirectToAction("/Index", "Dashboard");` (the leading slash) but its then going to have other impacts. And a user entering just `../Dashboard` in the address bar expects to hit the `Index` method of `DashboardController`, but instead your defining routes to ensure it goes to `UserController` (and probably throw an exception) –  Mar 20 '17 at 23:22

1 Answers1

0

Your routing is correct (at least for the URLs you provided). But you are not calling the redirect correctly.

When you generate an outgoing route, it doesn't match a URL, it matches the route values that are passed. Your UserRoute contains 3 route values:

  • username
  • Controller
  • Action

So, in order to generate a URL (or redirect) based on this route, you need to pass all 3 parameters.

return RedirectToAction("Index", "Dashboard", new { username = "Fred" });

That said, MVC will automatically reuse route values if they are in the current request. So, if your route already has a username value (for example, you are already at the URL /Fred), it will be able to match the route without specifying the route value explicitly.

return RedirectToAction("Index", "Dashboard");
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • but I don't want to pass unnecessary parameter :) – Oleg Sh Mar 20 '17 at 22:51
  • The parameter is not *unnecessary*, it is *required* because you have it in your URL signature. But you can pass it by other means. Perhaps [this](http://stackoverflow.com/questions/36996021/how-to-change-route-to-username-after-logged-in) is more along the lines of what you are trying to do? – NightOwl888 Mar 20 '17 at 23:41
  • parameter is unnecessary for action :) – Oleg Sh Mar 21 '17 at 00:27