I've declared Index action in Home controller:
[HttpGet]
public ActionResult Index(string type)
{
if (string.IsNullOrEmpty(type))
{
return RedirectToAction("Index", new { type = "promotion" });
}
return View();
}
That accepts:
https://localhost:44300/home/index?type=promotion
and
https://localhost:44300/?type=promotion
Everything was ok until I config route for 404 page:
routes.MapRoute(
name: "homepage",
url: "home/index",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "default",
url: "/",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
"404-PageNotFound",
"{*url}",
new { controller = "Error", action = "PageNotFound" }
);
Invalid syntax:
The route URL cannot start with a '/' or '~' character and it cannot
contain a '?' character.
If I remove the second configuration,
https://localhost:44300/?type=promotion
wouldn't be accepted. -> Show 404 page.
My question is: Is there a way to config route URL start with '/' (none controller, none action)?
Your route is misconfigured, as the error states it cannot begin with a /, and for the home page it doesn't need to. In that case it should be an empty string.
routes.MapRoute(
name: "default",
url: "",
defaults: new { controller = "Home", action = "Index" }
);
However, it is a bit unusual (and not SEO friendly) to want to map more than one route to the home page of the site as you are doing.
It is also unusual to do a redirect to a home page, which does an additional round trip across the network. Usually routing directly to the page you want will suffice without this unnecessary round trip.
routes.MapRoute(
name: "homepage",
url: "home/index",
defaults: new { controller = "Home", action = "Index", type = "promotion" }
);
routes.MapRoute(
name: "default",
url: "/",
defaults: new { controller = "Home", action = "Index", type = "promotion" }
);
// and your action...
[HttpGet]
public ActionResult Index(string type)
{
return View();
}
Related
In my web api, I have created this controller:
public class DistributionGroupController : ApiController
{
[HttpGet]
public ServiceResult Index(string id)
{
if (id == null)
return null;
else
return new ServiceResult();
}
}
In addition, this is my route config. I am specifying my default action for my distribution groups route to be "Index":
routes.MapHttpRoute(
"Api action",
"Api/{controller}/{action}"
);
routes.MapHttpRoute(
"Api get",
"Api/{controller}"
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
/*This is the route in question*/
routes.MapHttpRoute(
"DistGroupRoute",
"api/distributiongroup/{id}/{action}",
new { controller = "DistributionGroup", action = "Index" }
);
And in my view, I am using this script to (try to) hit my controller:
$.ajax({
url: "api/distributiongroup/4567bn57n5754",
cache: false,
success: function (response) {
alert('success');
}
});
But my ajax call recieves a 404 Not Found error. However, if I append index to my url from my ajax call, my controller is hit. So, in essence, this does not work:
api/distributiongroup/4567bn57n5754
But this does work:
api/distributiongroup/4567bn57n5754/index
It's my understanding that my default action should get hit if I don't specify my action in my url. What might I be missing here? And, more importantly, how can I make my Index controller get hit when I use a url such as this:
api/distributiongroup/4567bn57n5754
(without specifying the Index action?
routes.MapHttpRoute(
"DistGroupRoute",
"api/distributiongroup/{id}",
new { controller = "DistributionGroup", action = "Index" }
);
instead of:
routes.MapHttpRoute(
"DistGroupRoute",
"api/distributiongroup/{id}/{action}",
new { controller = "DistributionGroup", action = "Index" }
);
There is no need to add the {action} in the route template because you already added it in the defaults object.
Basically, you said:
Whenever there is an URL that matches this route template (api/distributiongroup/{id}/{action}) trigger the Index action in the DistributionGroup controller and pass the id parameter.
This happened because of the order I was specifying my routes in my route config. I was specifying them in this order:
routes.MapHttpRoute(
"Api action",
"Api/{controller}/{action}"
);
routes.MapHttpRoute(
"Api get",
"Api/{controller}"
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapHttpRoute(
"DistGroupRoute",
"api/distributiongroup/{id}/{action}",
new { controller = "DistributionGroup", action = "Index" }
);
Is seems that this route (my default route):
{controller}/{action}/{id}
was overriding my distributiongroups route when I did not specify index in my url. I'm still not entirely sure why this happened. But re-ordering my route configs fixed it. I just needed to put my distributiongroup route before my default route:
routes.MapHttpRoute(
"DistGroupRoute",
"api/distributiongroup/{id}/{action}",
new { controller = "DistributionGroup", action = "Index" }
);
routes.MapHttpRoute(
"Api action",
"Api/{controller}/{action}"
);
routes.MapHttpRoute(
"Api get",
"Api/{controller}"
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I am having trouble implementing the routing in MVC 5. While debugging the expected url(e.g. http://localhost/Download/Blog/1cf15fe6033a489a998556fedeab20a2/Test/1cd15fe6033a489a998556fedeab20a2) causes the correct method on the Download controller to be called however the did and fid are always null. What am I doing wrong? I also tried removing the Download route and defining the routes in the controller with the following attributes:
[RoutePrefix("Download")] //on the controller
[Route("{action}/{did:guid}/Test/{fid:guid}")] //on the Blog Action
Here is what I have in my RouteConfig.cs:
routes.MapRoute(
name: "Download",
url: "Download",
defaults: new { controller = "Download", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);
here is my controller:
[Route("{action}/{did}/Test/{fid}")]
public class DownloadController : Controller
{
public ActionResult Index()
{
return Redirect(HandleBadResponse(webResponse));
}
[Route("{action}/{did}/Test/{fid}")]//nope
public ActionResult Blog(HttpRequestMessage request,string did,string fid)
{
string server = Request.ServerVariables["SERVER_NAME"];
string pathStr = #"\\mypath\1cf15fe6033a489a998556fedeab20a2.xls";
byte[] fileBytes = System.IO.File.ReadAllBytes(pathStr);
string fileName = "test.txt";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
I got the expected result by adding the following to my RouteConfig.cs I placed this route at the top of my RegisterRoutes method, I think you should go from most detailed route to the least detailed route:
routes.MapRoute(
name: "DownloadBlogAttachment",
url: "Download/Blog/{did}/fid/{fid}",
defaults: new { controller = "Download", action = "Blog"}
);
I removed the Route attributes in my controller as well.
For www.demo.com/city/hotel-in-city
routes.MapRoute(
name: "Search",
url: "{city}/{slug}",
defaults: new { controller = "Demo", action = "Index", city = UrlParameter.Optional}
);
For default
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
But when I call the index method of home controller www.demo.com/home/index it points to 1st route(index method of default controller).
How to handle default route ?
The problem is that your "Search" route captures basically everything. One way of handling this is to create more-specific routes for the home controller, and put those first:
routes.MapRoute(
name: "Home1",
url: "/",
defaults: new { action = "Index", controller = "Home" }
);
routes.MapRoute(
name: "Home2",
url: "Home/{action}/{id}",
defaults: new { id = UrlParameter.Optional, action = "Index", controller = "Home" }
);
routes.MapRoute(
name: "Search",
url: "{city}/{slug}",
defaults: new { controller = "Demo", action = "Index" }
);
This will filter out any URL with "Home" as the first parameter, and allow everything else through to the search.
If you have a lot of controllers, the above approach may be inconvenient. In that case, you could consider using a custom constraint to filter out either the default route, or the "Search" route, whichever one you decide to put first in the route config.
For example, the following constraint declares the match invalid, in case the routing engine has attempted to assign "Home" to the "city" parameter. You can modify this as needed, to check against all your controllers, or alternately, against a cached list of available city names:
public class SearchRouteConstraint : IRouteConstraint
{
private const string ControllerName = "Home";
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return String.Compare(values["city"].ToString(), ControllerName, true) != 0;
}
}
This will allow URLs starting with "/Home" through to the default route:
routes.MapRoute(
name: "Search",
url: "{city}/{slug}",
defaults: new { controller = "Demo", action = "Index" },
constraints: new { city = new SearchRouteConstraint() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional, action = "Index", controller = "Home" }
);
We have a route config like so:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "About",
url: "{controller}/{action}/{aboutId}",
defaults: new { controller = "Home", action = "About" }
);
routes.MapRoute(
name: "Contact",
url: "{controller}/{action}/{contactId}",
defaults: new { controller = "Home", action = "Contact" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
You will notice that there are two routes that have one mandatory extra parameter. The routes About and Contact.
In our app we have two urls
www.myapp.com/Home/About/2 which works fine.
But when we navigate our browser to www.myapp.com/Home/Contact/5 we get the dreaded routing exception:
The parameters dictionary contains a null entry for parameter 'contactId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Contact(Int32)' in 'RoutingTest.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
If we change the sequence of the routing so that it looks like so:
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Contact",
url: "{controller}/{action}/{contactId}",
defaults: new { controller = "Home", action = "Contact" }
);
routes.MapRoute(
name: "About",
url: "{controller}/{action}/{aboutId}",
defaults: new { controller = "Home", action = "About" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Then the Contact url works but the About url does not.
The HomeController looks like this:
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult About(int aboutId) {
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact(int contactId) {
ViewBag.Message = "Your contact page.";
return View();
}
}
What this seems to imply is that two routings cannot have the same number of parameters regardless of the name of the Controller Action. If the two controller actions have a parameter with the same name, then all works fine. I know I can start doing very hacky things to work around this problem such as calling all parameters the same name or giving the actions meaningless parameters to change the number of parameters but I would actually like to know what is happening under the hood.
How do I solve this problem?
The root (no pun intended) of your issue ISN'T that routes can't have the same number of params. They can. The issue is that the routing engine will select the first route that matches the incoming request. Your routes are only different by the defaults, and pattern matching-wise they are exactly the same. So in each and every case you should be hitting the Contact route.
It looks like you are trying to have different routes based on the action. Which I can't actually see why you NEED.
You CAN use the following for that effect.
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Contact",
url: "Home/Contact/{contactId}"
);
routes.MapRoute(
name: "About",
url: "Home/About/{aboutId}"
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
However. I HIGHLY recommend against this approach, as your "default" route would be the Contact route. This means that (under Razor) the #Html.ActionLink() and related methods will be...wrong.
Honestly, it should just work perfectly if you actually just use...
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Specify controller and/or action in a route explicitly to allow routing to pick correct route:
routes.MapRoute(
name: "About",
url: "{controller}/About/{aboutId}",
defaults: new { controller = "Home", action = "About" }
);
You also can add constraints to parameters to distinguish between routes, but it looks like in your case both actions have the same integer parameter.
Couldn't you just delete your custom routes and just reuse the {id} parameter (turn {contactId} and {aboutId} into just "id" in your action code)?
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult About(int id) {
int aboutId = id;
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact(int id) {
int contactId = id;
ViewBag.Message = "Your contact page.";
return View();
}
}
My Booking Controller have the following code
public ActionResult Index(string id, string name)
{
return View();
}
and my routeConfig have the below route mappings
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Search",
url: "{controller}/{location}/{checkIn}/{checkOut}/{no}",
defaults: new { controller = "Search", action = "Index", location = UrlParameter.Optional, checkIn = UrlParameter.Optional, checkOut = UrlParameter.Optional, no = UrlParameter.Optional }
);
routes.MapRoute(
name: "booking",
url: "{controller}/{action}/{id}/{name}",
defaults: new { controller = "Booking", action = "Index", id = UrlParameter.Optional, name=UrlParameter.Optional }
);}
but when I access the page http://localhost:59041/booking/index/1/libin both params returns null.
see this book
As your application becomes more complex you are likely going to
register multiple routes. When you do this its important that you
consider the order that that you register them. When the routing
engine attempts to locate a matching route, it simply enumerates the
collection of routes and it stops enumerating as soon as it find a
match.
Add a comment This can cause plenty of problems if you’re not
expecting it. Let’s look at an examples where this can be a problem:
routes.MapRoute(
> "generic", // Route name
> "{site}", // URL with parameters
> new { controller = "SiteBuilder", action = "Index" } // Parameter defaults );
>
> routes.MapRoute(
> "admin", // Route name
> "Admin", // URL with parameters
> new { controller = "Admin", action = "Index" } // Parameter defaults );
The snippet above registers two routes. The first route
contains a single placeholder segment and sets the default value of
the controller parameter to SiteBuilder. The second route contains a
single constant segment and sets the default value of the controller
parameter to Admin.
Both of these routes are completely valid, but the order in which they
are mapped may cause unexpected problems because the first route
matches just about any value entered, which means that it will be the
first to match
http://example.com/Admin and since the routing engine stops after
finding the first match, the second route would never get used.
So, be sure to keep this scenario in mind and consider the order in
which you define custom routes.
You should write booking routes at first
routes.MapRoute(
name: "booking",
url: "{controller}/{action}/{id}/{name}",
defaults: new { controller = "Booking", action = "Index", id = UrlParameter.Optional, name=UrlParameter.Optional }
);}
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Search",
url: "{controller}/{location}/{checkIn}/{checkOut}/{no}",
defaults: new { controller = "Search", action = "Index", location = UrlParameter.Optional, checkIn = UrlParameter.Optional, checkOut = UrlParameter.Optional, no = UrlParameter.Optional }
);