Here is my route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{testId}/{lg}",
defaults: new { controller = "Home", action = "Index", testId = UrlParameter.Optional, lg = UrlParameter.Optional }
);
Here is my controller code
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index(int testId,string lg)
{
return View();
}
public ActionResult Index2(int testId, string lg)
{
return View();
}
}
Here is my view code (Index.cshtml)
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Url.Action("Index")<br/>
#Url.Action("Index2")
When I open "/Test/INdex/1/EN"
This gives me
/Test/Index/1/EN
/Test/Index2
The first link is ok because it uses the current route value (lg =EN and testId = 1)
But the second is not using the values, I don't get it !
I wouldn't have expected the first link to have route values, honestly. Maybe the view engine is smart enough to know that it's linking to the current route and automatically populates them?
In any event, linking to something entirely different isn't going to populate route values by default. The framework has no reason to believe that those route values are applicable to an entirely different action.
Specify them explicitly:
#Url.Action("Index2", new { testId = 1, lg = "EN" })
I found a workaround : the route parameters must be before the action ! :
routes.MapRoute(
name: "Default",
url: "{controller}/{testId}/{lg}/{action}",
defaults: new { controller = "Home", action = "Index", testId = UrlParameter.Optional, lg = UrlParameter.Optional }
);
If someone can say me why...
Related
I am having trouble with routing in MVC. I have created a controller for my contact page, but unless I specify the route as /contact/index it will return a 404. I cannot see why it can't find the View with just /contact in the URL. My RouteConfig looks fine to me.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "T",
url: "T/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Holding",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Holding", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
The only reason I can see it not finding its View is because of the new Route I have configured to display a site holding page. Interestingly /t does display the 'demo' homepage, so I can't see why it doesn't like just /contact.
This S.O article told me that I could fix the problem by giving it its own MapRoute but I shouldn't have to do all that?
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Holding()
{
return View();
}
}
public class ContactController : Controller
{
// GET: Contact
public ActionResult Index()
{
return View();
}
}
It must be something silly, but I can't work it out.
You have route conflicts
/contact would match
routes.MapRoute(
name: "Holding",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Holding", id = UrlParameter.Optional }
);
But since contact controller has no Holding action you will get a 404 Not Found
And since it matched the Holding route it wont go on to the next Default route as first match wins.
The added route is too general so it will get a lot of false matches.
Based on the controllers shown, the added route is not needed. the holding path would still match the default route template. So it can actually be removed altogether.
My question is very similar other questions. When using an ActionLink in MVC .Net 4.5, I am getting a query string for one parameter, instead of just a URL path. I tried the solution HERE, but it did not work.
CODE-
Inside RouteConfig.cs -
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "MyControllerRoute",
url: "{controller}/{action}/{id}/{description}",
defaults: new { controller = "MyController", action = "MyAction", id = UrlParameter.Optional, description = UrlParameter.Optional }
);
Inside HomeController.cs -
public ActionResult Index(){
--do stuff--
return View();
}
Inside MyController.cs -
public ActionResult Vote(int id, string description){
--do stuff--
return View();
}
Inside Index.cshtml
#Html.ActionLink(
"This is stuff",
"MyAction",
"MyController",
new { id = 123, description = "This-is-stuff" },
null)
GETTING THIS RESULT - (NOT WHAT I WANT)
This is stuff
DESIRED RESULT - (HOW CAN I GET THIS?)
This is stuff
You need to swap the order of the routes. I would also recommend that you use the controller name (and optionally the action name) in the url definition to prevent possible conflicts with other routes. In addition, only the last parameter can be marked as UrlParameter.Optional (otherwise if only one of the parameters were provided, the route would be ignored and the url would revert to using query string values). Your definitions should be (in order)
routes.MapRoute(
name: "MyControllerRoute",
url: "MyController/MyAction/{id}/{description}",
defaults: new { controller = "MyController", action = "MyAction" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Question background:
I'm trying to pass an variable - in this case an int 'productId' variable' in the url to a controller and action method specified in the ActionLink method.
The issue:
My routes are set as follows:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Login", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "ProductDetailHandler",
url: "{controller}/{action}/{productId}",
defaults: new { controller = "Product", action = "ProductDetail", productId = UrlParameter.Optional }
);
My 'ActionLink' in my 'Products.cshtml' view - which is returned by a controller called 'HomePageController' is set as follows:
#Html.ActionLink("Product", "ProductDetail", new { productId = (ViewBag.data as List<LoginTest.Models.HomePageItem>).First().Id })
The controller that receives the passed 'productId' along with its action method is set as follows:
public class ProductController : Controller
{
public ActionResult ProductDetail(int productId)
{
//logic
return View();
}
}
This is the issue, when looking at the URL it is shown to be redirecting to the 'HomePage' controller:
If someone could tell me why my ActionLink is not going to the Product controller that would be great.
EDIT:
This is the 'Homepage' view that I click a button to redirect me to 'product/productDetail/productId'
My route now just features the 'Default example':
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Login", id = UrlParameter.Optional }
);
The Action Link is now:
#Html.ActionLink("Product", "ProductDetail", "Product", new { id = (ViewBag.data as List<LoginTest.Models.HomePageItem>).First().Id })
The 'Product/ProductDetail' controller and action method now looks like:
public class ProductController : Controller
{
public ActionResult ProductDetail(int id)
{
string hold;
return View();
}
}
This still is giving me the the incorrect URL, as shown, note the 'productId' is now showing as 'length'?
Since the link is on a page rendered by HomePageController the default is to use that controller in the route. You need to use the overload that accepts a controller name
#Html.ActionLink("Your link text", "ProductDetail", "Product", new { id = 1 }, null)
As a side note, your original route table would have created /Product/ProductDetail?productId =1 with this overload because it matches the default route which is the first route in your table (the order of routes is important). In order to have /Product/ProductDetail/1, either reverse the order of the routes or just change the parameter of ProductDetail to int id rather than int productId
Make sure you are using an overload that has controllerName in it, as shown in the following screenshot.
When I remove routeValues: null, it uses a different overlaod which has routeValue as third paramater.
Try this:
routes.MapRoute(
name: "ProductDetailHandler",
url: "Product/{action}/{productId}",
defaults: new { controller = "Product", action = "ProductDetail", productId = UrlParameter.Optional }
);
routes.MapRoute
(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Not sure what you are trying to do with the Login controller. Maybe you can put log-in on your Home page or redirect the Home/Index to Login.
Also you can specify the default namespace if it doesn't find your controller:
routes.MapRoute(
name: "ProductDetailHandler",
url: "Product/{action}/{productId}",
defaults: new { controller = "Product", action = "ProductDetail", productId = UrlParameter.Optional },
new string[] { "MyProject.Controllers" }
);
routes.MapRoute
(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "MyProject.Controllers" }
);
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?
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 }
);
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
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.
I don't think there is a solution for your requirements, as you have two competing routes. Maybe you can define something specific (in case you don't have any other controllers).
routes.MapRoute(
name: "Send",
url: "{controller}/Send/{id}",
defaults: new { controller = "Home", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Home",
url: "Home",
defaults: new { controller = "Home", action = "Index" }
routes.MapRoute(
name: "Index",
url: "{controller}/{id}",
defaults: new { controller = "Home", action= "Index",
id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
This what worked for me:
1) In RouteConfig, I put this to be the first line:
routes.MapRoute(name: "single", url: "{controller}/{lastName}",
defaults: new { controller = "LeaveRequests", action = "Index" });
2) In my controller:
public ViewResult Index(string lastName)
{
if (lastName == null)
{
return //return somthing
}
else
{
return //return something
}
}
3) when you call the controller
http://localhost:34333/leaveRequests/Simpsons
it will give you all requests for Simpsons.
if you call http://localhost:34333/leaveRequests/
it will give all requests
this is working for me
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default1",
url: "{id}",
defaults: new { controller ="Home", action ="Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller ="Home", action ="Index", id = UrlParameter.Optional }
);
public class PostController : YourDefinitionController
{
public ActionResult Test(int id ,int title)
{
return View();
}
}
#Html.ActionLink("Postss", "Test","Post" ,new { title = "asdf",id=3 },null)//in Razor view
// here is route registration
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Post",
"{Post}/{Test}/{title}/{id}",
new { controller = "Post", action = "Test",id=UrlParameter.Optional, title=UrlParameter.Optional }
);
routes.MapRoute(
"Defaultx", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
I expected to see link like /Post/Test/asdf/3 but it is /Post/Test/3?title=3
Why ? How can i fix it?
I would suggest cleaning your code a bit, because there are many things done based on conventions. So keeping code consistent often helps.
public ActionResult Test(string title ,int id) // Order is switched and title changed to string
Edit: The problem is with wrong route path. You have to change it to "Post/Test/{title}/{id}"
// changed route path expression
routes.MapRoute(
"Post",
"Post/Test/{title}/{id}",
new { controller = "Post", action = "Test", title = UrlParameter.Optional, id = UrlParameter.Optional }
);
Btw: If you are going to play with routes a bit more, Phil Haack's blog will be a great resource.