Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Routing to index with id in ASP.NET MVC 4

I'd like to maintain ASP.NET MVC 4's existing controller/action/id routing with default controller = Home and default action = Index, but also enable controller/id to route to the controller's index method as long as the second item is not a known action.

For example, given a controller Home with actions Index and Send:

/Home/Send -> controller's Send method
/Home -> controller's Index method
/Home/Send/xyz -> controller's Send method with id = xyz
/Home/abc -> controller's Index method with id = abc

However, if I define either route first, it hides the other one. How would I do this?

like image 491
David Pfeffer Avatar asked Dec 11 '12 19:12

David Pfeffer


3 Answers

Do the specific one first before the default generic one. The order matters.

routes.MapRoute(name: "single", url: "{controller}/{id}",
    defaults: new { controller = "Home", action = "Index" }, 
    constraints: new { id = @"^[0-9]+$" });

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home",
    action = "Index",
    id = UrlParameter.Optional }
);
like image 65
Shyju Avatar answered Oct 12 '22 07:10

Shyju


In case, that the list of your actions (e.g. Send) is well known, and their (action) names cannot be the same as some ID value, we can use our custom ConstraintImplementation:

public class MyRouteConstraint : IRouteConstraint
{
  public readonly IList<string> KnownActions = new List<string> 
       { "Send", "Find", ... }; // explicit action names

  public bool Match(System.Web.HttpContextBase httpContext, Route route
                  , string parameterName, RouteValueDictionary values
                  , RouteDirection routeDirection)
  {
    // for now skip the Url generation
    if (routeDirection.Equals(RouteDirection.UrlGeneration))
    {
      return false; // leave it on default
    }

    // try to find out our parameters
    string action = values["action"].ToString();
    string id = values["id"].ToString();

    // id and action were provided?
    var bothProvided = !(string.IsNullOrEmpty(action) || string.IsNullOrEmpty(id));
    if (bothProvided)
    {
      return false; // leave it on default
    }

    var isKnownAction = KnownActions.Contains(action
                           , StringComparer.InvariantCultureIgnoreCase);

    // action is known
    if (isKnownAction)
    {
      return false; // leave it on default
    }

    // action is not known, id was found
    values["action"] = "Index"; // change action
    values["id"] = action; // use the id

    return true;
  }

And the route map (before the default one - both must be provided), should look like this:

routes.MapRoute(
  name: "DefaultMap",
  url: "{controller}/{action}/{id}",
  defaults: new { controller = string.Empty, action = "Index", id = string.Empty },
  constraints: new { lang = new MyRouteConstraint() }
);

Summary: In this case, we are evaluating the value of the "action" parameter.

  • if both 1) action and 2) id are provided, we won't handle it here.
  • nor if this is known action (in the list, or reflected...).
  • only if the action name is unknown, let's change the route values: set action to "Index" and action value to ID.

NOTE: action names and id values need to be unique... then this will work

like image 25
Radim Köhler Avatar answered Oct 12 '22 06:10

Radim Köhler


The easiest way is to simply create two Action methods in your Controller. One for Index and one for Send and place your string id parameter on both. Since you cannot have duplicate or overloaded action methods, that solves that problem. Your Index method will now handle both index or blank paths where the id is present or not (null) and process your views that way. Your Send method will do the exact same as Index. You can then route, process, or redirect how you like, depending on if id is null. This should work with no changes to RouteConfig.cs:

public ActionResult Index(string id) {if (id == null) Do A else Do B}

public ActionResult Send(string id) {if (id == null) Do A else Do B}

I had this struggle for a long time, and this was the simplest solution.

like image 44
Stokely Avatar answered Oct 12 '22 07:10

Stokely