My MVC4 website shows items from a database The user can 'refine' their search from within a web form. After this, they click the search button and their results are shown.
At this stage, I only have 1 route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/",
defaults: new { controller = "Home", action = "Index" }
);
When I load the page for the first time, the address is www.mydomain.com/products/connectors/, after I make a search it appends my querystring
www.mydomain.com/products/connectors/?Gender=1
Now, I'm adding pagination and would like the user to be able to select Next page. I've used Marc's answer from How do I do pagination in ASP.NET MVC? to do this. Pagination works great.
This is the routeconfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "AllRefineSearch",
url: "{controller}/{action}/{startIndex}/",
defaults: new { controller = "Products", action = "Connectors", startIndex = 0, pageSize = 10 }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/",
defaults: new { controller = "Home", action = "Index" }
);
}
}
The issue though is now when I click the search button, my Controller and Action are removed from the address. In other words, the address is www.mydomain.com/?Gender=1
I don't know how to keep the controller and action in the URL as I thought the route was specifying this!
My form is
#using (Html.BeginForm("Connectors", "Products", FormMethod.Get))
{
#Html.DropDownListFor(a => a.Gender, Model.ConnectorRefineSearch.Gender, "---Select---")
<input type="submit" value="Search" class="searchButton" />
}
And my controller
[HttpGet]
public ActionResult Connectors(ConnectorVm connector, int startIndex, int pageSize)
{
connector.UpdateSearch();
return View(connector);
}
The problem is your AllRefineSearch route with its default values: "Products" controller, "Connectors" action and "0" startIndex.
Providing default values in a route also means that those segments are optional. In your case the url "/" will match that route, and each segment will take its default value.
A similar process is followed when generating a Url for a link,form, etc. You are providing "Products" as controller and "Connectors" as action, so the AllRefineSearch will be used. Because they are the default values, it will simplify the url for the form tag as "/". Finally on submit the inuput values are added in the query string.
Try without providing default values in the search route for the controller and action segments:
routes.MapRoute(
name: "AllRefineSearch",
url: "{controller}/{action}/{startIndex}/",
defaults: new { startIndex = 0, pageSize = 10 }
);
Related
I use VS2013 and created MVC application by wizard. I also deleted all extra files and have the following:
1) RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
2) HomeController.cs
public class HomeController : Controller
{
[Route("Home/Index")]
public ActionResult Index()
{
return View();
}
}
3) Index.cshtml
#{
ViewBag.Title = "Home Page";
}
Home page
I've got the page with error:
HTTP 403.14 - Forbidden
But, if I add to URL in browser's address bar manually - Home/Index:
http://localhost:50600/Home/Index
The page appears.
What I'm doing wrong?
Remove the "Home" from the route as the controller name HomeController is already starting your route with "Home". If you want to change that "Home" prefix, you can add an attribute to the HomeController class to define that.
Also, the default route name for an action will match the action name, so in this case you could use [Route("")] and the url /Home/Index would work.
My guess is that when you try this url:
http://localhost:50600
It does not work because you have removed the default route from your routes config. I don't know if you removed it yourself, but RoutesConfig.cs file usually comes with the following default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This code ensures that if the user does not provide the controller or the action the site will default to index action of the home controller (you ca see that under the defaults parameter). This would also explain why it works when you try this route:
http://localhost:50600/Home/Index.
I think I know what's your problem now. You're expecting the default url to show up your Index View in HomeController but you've not setup the default route. You can set the default route by adding the following lines in your RouteConfig.cs
config.Routes.MapRoute(
name: "Default",
routeTemplate: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
Alternatively, if you wish to use attribute routing only without mixing with route template, you can just add the default route as following:-
config.Routes.MapRoute(
name: "Index",
url: "",
defaults : new { controller = "Home", action = "Index" }
);
First of all, I am very new to MVC and this is my first ever project.
I am trying to achieve the custom routing URL like the following:
http://mywebsite/MDT/Index/ADC00301SB
Similar to...
http://mywebsite/{Controller}/{Action}/{query}
In my RouteConfig.cs, I put the following
routes.MapRoute(
name: "SearchComputer",
url: "{controller}/{action}/{query}",
defaults: new { controller = "MDT", action = "Index", query = UrlParameter.Optional }
);
In My MDTController.cs, I have the following code
public ActionResult Index(string query)
{
Utils.Debug(query);
if (string.IsNullOrEmpty(query) == false)
{
//Load data and return view
//Remove Codes for clarification
}
return View();
}
But it's not working and I always get NULL value in query if I used http://mywebsite/MDT/Index/ADC00301SB
But if I used http://mywebsite/MDT?query=ADC00301SB, it's working fine and it hits the Controller Index method.
Could you please let me know how I could map the routing correctly?
You should add your MapRoute before default MapRoute, because order in RouteCollection is important
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "SearchComputer",
url: "{controller}/{action}/{query}",
defaults: new { controller = "MDT", action = "Index", query = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
One issue that I have encountered is that placing your route below the default route will cause the default to be hit, not your custom route.
So place it above the default route and it will work.
A detailed explanation from MSDN:
The order in which Route objects appear in the Routes collection is significant. Route matching is tried from the first route to the last route in the collection. When a match occurs, no more routes are evaluated. In general, add routes to the Routes property in order from the most specific route definitions to least specific ones.
Adding Routes to an MVC Application.
You can change it to
routes.MapRoute(
name: "SearchComputer",
url: "MDT/{action}/{query}",
defaults: new { controller = "MDT", action = "Index", query = UrlParameter.Optional }
);
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Letter",
url: "{Home}/{Letter}/{ListId}",
defaults: new { controller = "Home", action = "Letter", ListId=1}
);
routes.MapRoute(
name: "words",
url: "{Home}/{words}/{WListId}",
defaults: new { controller = "Home", action = "words", WListId ="w1" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id= UrlParameter.Optional }
);
}
cshtml:
#Html.ActionLink("Home", "Index", "Home")
#Html.ActionLink("Letter", "Letter/1", "Home")
#Html.ActionLink("Words", "words/w1", "Home")
I am doing this in route.config and .cshtml respectively but every time it redirects me to the letter page even when i click on "words" or "home". When I click words or home it changes the url but does not change the view. Can any one suggest how to give multiples route in route.config file? What's wrong with this code ?
I'm completely revamping this because I think I now see what you are trying to do.
An ActionLink works as a helper to render an anchor element. So using
#Html.ActionLink("Link", "Action", "Controller")
helper, your page renders something in the form of:
Link
What you want then, is to write the proper controller and action values - you don't need routes for this. So in order to produce a link for Home/words/1, you can use the ActionLink helper (with the default route only) like this:
#Html.ActionLink("Words", "Words", "Home", new { WListId = "w1" })
This will produce:
/Home/Words/w1
and in your HomeController.cs, your action must look like:
public ActionResult Words(string WListId)
{
// whatever you want to do with WListId
return View();
}
and your View must be named Words.cshtml
The same goes for Letter as well. For this, all you need is the one Default Route that's already there.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id=UrlParameter.Optional });
I'm having problems with routing. I have News controller and I can read the details of a news from url http://localhost/News/Details/1/news-title(slug). Here is no problem for now. But I created a controller called Services with Index action. Route config:
routes.MapRoute(
"Services",
"Services/{id}",
new { controller = "Services", action = "Index" }
);
When my Index Action is
public ActionResult Index(string title)
{
return View(title);
}
I write localhost:5454/Services/sometext by hand in browser it works.
But when change my index action to
public ActionResult Index(string title)
{
Service service = _myService.Find(title);
ServiceViewModel = Mapper.Map<Service , ServiceViewModel >(service );
if (service == null)
{
return HttpNotFound();
}
return View(serviceViewModel);
}
I get Http Error 404 Not Found for Url localhost/Services/ITServices.
I can add this services from admin page with it's title (ITServices for example).
Then I do foreach in my Home Page for the services links
#foreach (var service Model.Services)
{
<div class="btn btn-success btn-sm btn-block">
#Html.ActionLink(service.Title, "Index", new { controller = "Services", id = service.Title })
</div>
}
But I can't show the page in localhost/Services/ITServices.
When I click on the link I want to go to page localhost/Services/ITServices and it has to show the content(which can be added from admin page) like in the news. But I don't want to use it with action name and ids like in the news. How can I achieve this?
EDIT
Ok. I add FindByTitle(string title) in repository. I changed the id to title in RouteConfig and in the link in Home Page View. Then in my domain model deleted Id and updated Title as [Key]. Now it works. Only have to check with remote validation for possible duplicates of Title when adding new from admin page.
The parameter name in the URL template does not match the argument on the Action (Index).
So you can do one of two things.
Change the template parameter to match the argument of the Action
routes.MapRoute(
name: "Services",
url: "Services/{title}",
defaults: new { controller = "Services", action = "Index" }
);
Where Action Index is
public ActionResult Index(string title) { ... }
or you change the argument of the Action Index to match the paramater in the url template
public ActionResult Index(string id) { ... }
Where the route mapping is
routes.MapRoute(
name: "Services",
url: "Services/{id}",
defaults: new { controller = "Services", action = "Index" }
);
But either way the route is not finding route because it cannot match the parameters.
In fact if the intention is to use the title as a slug you could go so far as to use a catch all route for services like
routes.MapRoute(
name: "Services",
url: "Services/{*title}",
defaults: new { controller = "Services", action = "Index" }
);
Take a look at the answers provided here
how to route using slugs?
Dynamic routing action name in ASP.NET MVC
Try this:
public ActionResult Index(string id)
I am creating a multi-tenant asp.net application. I want my url to follow
**http://www.example.com/test1/test2/**{tenantName}/{controller}/{action}
**http://www.example.com/test1/**{tenantName}/{controller}/{action}
**http://www.example.com/**{tenantName}/{controller}/{action}
Here the part of the url in bold is fixed (will not change)
{tenantName}=will be logical tenant instance.
I have followed this link
What will be the routing to handle this?
It's as simple as this:
routes.MapRoute(
"MultiTenantRoute", // Route name
"test1/test2/{tenantName}/{controller}/{action}/{id}", // URL with parameters
new { id = UrlParameter.Optional } // Parameter defaults, if needed
);
The part without braces must match. The parts inside the braces will be transfer into route data parameters. I've added an optional parameter id, as you usualy find in the controllers, but you can customize it. You can also give default values to tenantName, controller or action as usual.
Remember that routes are evaluated in the order they're registered, so you should probably register this route before any other.
EDIT after question update
You cannot specify a catch all parameter like this: {*segment} at the beginning of a route. That's not possible. ASP.NET MVC wouldn't know how many segments to include in this part, and how many to be left for the rest of the parameters in the route.
So, you need to add a route for each possible case,taking into account that the first route that matches will be used. So you'd need routes starting with extra parameters like this:
{tenanName}...
{segment1}{tenanName}...
{segment1}/{segment2}/{tenanName}...
Depending on the structre of the expected urls you may need to add constraints to ensure that the route is being correctly matched. This can be done passing a fourth parameter to thw MapRoute method. This is an anonymous class, like the deafults parameter, but the specified value for each parameter is a constraint. These constraints, on their simplest forma, are simply strings which will be used as regular expressions (regex).
If the expected URLs are extremely variable, then implement yout own routing class.
You could define the route as
routes.MapRoute(
name: "TennantRoute",
url: "test1/test2/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index"}
);
and your action must take parameter with name tenantName because you may want make some decision based on that ...for example
public ActionResult Index(string tenantName)
{
return View();
}
example : http://localhost:19802/test1/test2/PrerakT/Home/Index
Please make sure you define this path above the default route for following urls to work
http://localhost:19802/test1/test2/PrerakT/
http://localhost:19802/test1/test2/PrerakT/Home/
http://localhost:19802/test1/test2/PrerakT/Home/index
What if I want test1 and test2 to be changeable ...
routes.MapRoute(
name: "TennantRoute",
url: "{test1}/{test2}/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
and
public ActionResult Index(string tenantName, string test1, string test2)
{
return View();
}
as per your update on the question
routes.MapRoute(
name: "TennantRoute1",
url: "test1/test2/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "TennantRoute2",
url: "test1/{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "TennantRoute3",
url: "{tenantName}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);