The following code:
<li>#Html.ActionLink(metaTapp.Nav_About, "Mayla", "About")</li>
<li>#Html.ActionLink(metaTapp.Nav_Support, "Support", "About")</li>
<li>#Html.ActionLink(metaTapp.Nav_Exchange, "Index", "Exchange")</li>
<li>#Html.ActionLink("Post Rfq", "Create", "Rfq")</li>
is producing the following links:
<li>About</li>
<li>Support</li>
<li>Exchange</li>
<li>Post Rfq</li>
My Global Application Start looks like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
UploadRouteConfig.RegisterRoutes(RouteTable.Routes);
LocalizationConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory());
}
}
UploadRouteConfig.RegisterRoutes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Upload", "Upload/Image", null).RouteHandler = new UploadMvcRouteHandler();
}
LocalizationConfig.RegisterRoutes
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Account", // Route name
"Account/{action}", // URL with parameters
new { controller = "Account", action = "Index" } // Parameter defaults
);
routes.MapRoute(
"RfqCategory",
string.Format("{{{0}}}/Rfq/CategoryFilter/{{category}}", Constants.ROUTE_PARAMNAME_LANG),
new { controller = "Rfq", action = "CategoryFilter", category = Guid.Empty.ToString() }
);
routes.MapRoute(
Constants.ROUTE_NAME,
string.Format("{{{0}}}/{{controller}}/{{action}}/{{rfqid}}", Constants.ROUTE_PARAMNAME_LANG),
new { controller = "About", action = "Home", rfqid = "00000000-0000-0000-0000-000000000000" }
);
}
RouteConfig.RegisterRoutes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("TappDefault", "{controller}/{action}/{id}", new { controller = "About", action = "Home", id = UrlParameter.Optional }
);
I cant understand how upload is being matched to everything. If the route doesn't start with Upload/Image it should fall through to the localizationconfig routes?
Ok so the short answer to my problem is that I am rendering ActionLinks when I should be using RouteLinks. ActionLink will perform a match based on route table entries which seems like what I want but because I'm using a static Url:
"Upload/Image"
every url is matched. Why? Because routes are not filters. Routes work by matching supplied route values to the parameters of segments of the Url, but since:
"Upload/Image"
has no parameters i.e. {controller} then technically EVERYTHING is a match. RouteLink on tyhe pother hand allows me to specify which route to use when rendering the link:
#Html.RouteLink(
linkText: "route: Home",
routeName: "TappDefault",
routeValues: new {controller="About", action="Home"}
)
..
From Professional ASP.NET.MVC4 (Jon Galloway, Phil Haack, Brad Wilson, K. Scott Allen)
Page 232 Chapter 9 Routing:
Let’s suppose you add the following page route at the beginning of
your list of routes so that the URL /static/url is handled by the
page /aspx/SomePage.aspx:
routes.MapPageRoute("new", "static/url", "~/aspx/SomePage.aspx");
Note that you can’t put this route at the end of the list of routes
within the RegisterRoutes method because it would never match
incoming requests. Why wouldn’t it? Well, a request for /static/url
would be matched by the default route and never make it through
the list of routes to get to the new route. Therefore, you need to
add this route to the beginning of the list of routes before the
default route.
NOTE This problem isn’t specific to Routing with Web Forms. There are many cases where you might route to a non-ASP.NET MVC route
handler.
Moving this route to the beginning of the defined list of routes seems
like an innocent enough change, right? For incoming requests, this
route will match only requests that exactly match /static/url but will
not match any other requests. This is exactly what you want. But
what about generated URLs? If you go back and look at the result of
the two calls to Url.RouteLink, you’ll find that both URLs are broken:
/url?controller=section&action=Index&id=123
and
/static/url?controller=Home&action=Index&id=123
This goes into a subtle behavior of Routing, which is admittedly
somewhat of an edge case, but is something that people run into from
time to time.
Typically, when you generate a URL using Routing, the route values
you supply are used to “fill in” the URL parameters as discussed
earlier in this chapter.
When you have a route with the URL {controller}/{action}/{id}, you’re
expected to supply values for controller, action, and id when
generating a URL. In this case, because the new route doesn’t have
any URL parameters, it matches every URL generation attempt because
technically, “a route value is supplied for each URL parameter.” It
just so happens that there aren’t any URL parameters. That’s why all
the existing URLs are broken, because every attempt to generate a URL
now matches this new route.
This might seem like a big problem, but the fix is very simple. Use
names for all your routes and always use the route name when
generating URLs. Most of the time, letting Routing sort out which
route you want to use to generate a URL is really leaving it to
chance, which is not something that sits well with the
obsessive-compulsive control freak developer. When generating a
URL, you gener- ally know exactly which route you want to link to, so
you might as well specify it by name. If you have a need to use
non-named routes and are leaving the URL generation entirely up to
Routing, we recommend writing unit tests that verify the expected
behavior of the routes and URL generation within your application.
Specifying the name of the route not only avoids ambiguities, but it
may even eke out a bit of a per- formance improvement because the
Routing engine can go directly to the named route and attempt to use
it for URL generation.
In the previous example, where you generated two links, the following
change fixes the issue. We changed the code to use named parameters to
make it clear what the change was. #Html.RouteLink( linkText: "route:
Test", routeName: "test", routeValues: new {controller="section",
action="Index", id=123})
Related
I've been testing a few alternatives for a site that needs to support e.g.
site.com
site.com/Controller/Action/id
site.com/various.aspx
site.com/optionalparameter <-- these two go to the same controller
site.com/optionalparameter/ <-- these two go to the same controller
I actually need to verify if the optional parameter is acceptable, but I can direct it to a controller that will verify it. The other rules seem to work so far but I haven't been able to get the single optional parameter working yet.
The optional parameter in the url can be used with or without a slash "/" (at least without). I've found similar questions but they seem to have "optionalParam/xx/yy" so the rules have more parameters and they seem to work fine, but this is a bit of a special case converting a non-mvc site to use MVC routing.
An example of what I am trying to do, but isn't working.
routes.MapRoute(
"optionalParamRoute",
"{name}",
new { controller = "Home", action = "Index", name = "" }
);
Is this possible?
Try to move to top your optionalParamRoute parameter:
routes.MapRoute(
"optionalParamRoute",
"{name}",
new { controller = "Home", action = "Index", name = "" }
);
// other routes
Building my first ASP.NET MVC 3 application and trying to implement the ability to disassociate a given ice cream from a menu. Each has an integer identifier associated with it and on a page I display these mappings and provide a link by them to remove the ice cream from the menu.
I've got an ActionLink that looks like this:
#Html.ActionLink("Remove", "RemoveMenuIceCreamMapping", "IceCream", new { iceCreamId=item.IceCreamId, menuId=item.MenuId}, null)
In my IceCreamController I've got an Action that looks like this:
[HttpPost]
public PartialViewResult RemoveMenuIceCreamMapping(int iceCreamId, int menuId)
{
...
}
Did a little searching and believe I may need to modify the routes in the Global.asax.cs file's RegisterRoutes to handle these two parameters. So I tried this like so:
public static void RegisterRoutes(RoutesCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// I added this route in an attempt to handle the two parameters:
routes.MapRoute(
"RemoveMenuIceCreamMapping", // Route name
"IceCream/RemoveMenuIceCreamMapping/{iceCreamId}/{menuId}", // URLwith parameters
new
{
controller = "IceCream",
action = "RemoveMenuIceCreamMapping",
iceCreamId = UrlParameter.Optional,
menuId = UrlParameter.Optional
}
);
// this was there by default
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional }
};
}
But this doesn't work - I get a "The resource cannot be found." error, 404. Requested URL: /IceCream/RemoveMenuIceCreamMapping/1/10
1 is the Id of the IceCream and 10 is the menu's Id.
What I was expecting to happen was that the action RemoveMenuIceCreamMapping would get called, passing those two parameters, but I'm obviously not doing something right here and may just misunderstand how to accomplish what I want and be going about this the wrong way. Any guidance would be most appreciated.
Update
So, one more thing I've learned, after reading this SO question, my ActionLink isn't triggering a POST so removing the [HttpPost] from the action seemed like the right thing to do. And, in fact, as soon as I did that, the route was found and the action executed.
I think you problem is that the ActionLink uses an HTTP GET and you are only accepting HTTP POST.
You will probably need to change your view to issue an HTTP POST (e.g. with a regular HTML button inside a form) so that the verb that the browser sends matches with what you accept on the controller.
I need to create a custom routing for this url:
/par1/par2/par3/par99/11
The url must redirect to
home/index/11
11 is the Id
I need only this parameter, the other parameter (par1/par2/...) are only for SEO purpose and could be any word.
par1, par2, etc.. are created dynamically , so the Url could be:
/par1/par2/11
or
/par1/par2/par3/111
or
/par1/3
you could just turn it around and go with
routes.MapRoute(
"Route",
"{id}/{*seostuff}",
new {controller = "Home", action="Index", seo = UrlParameter.Optional});
that will allow you to map urls such as http://www.somesite.com/11/whatever/goes-here/will-be-whatever-you/want
Maybe you'll start with this and adjust to your needs:
routes.MapRoute(
"Def0", // Route name
"{controller}/{action}/{seo1}/{seo2}/{seo3}/{id}"
);
routes.MapRoute(
"Def1", // Route name
"{controller}/{action}/{seo1}/{seo2}/{id}"
);
routes.MapRoute(
"Def2", // Route name
"{controller}/{action}/{seo1}/{id}"
);
routes.MapRoute(
"Def3", // Route name
"{controller}/{action}/{id}"
);
Since your id is at the end, you would need to make 99 routes to handle all the stuff in between in order for the id route to the controller (easily).
I would stick the id to the left of the "SEO stuff" if possible, so it can be thrown away easier, exactly like #Morder described. The {*seostuff} parameter catches forward slashes ('/'), whereas thee {seo1}, {seo2} parameters do not.
look at the way stackoverflow does urls; after the id everything is thrown away.
I know this has already been accepted but I've written a route class that allows greedy segment to be anywhere in the URL definition.
In this case URL definition would be
{*seo}/{id}
Read all details about the custom route class and use it if you like:
Custom Asp.net MVC route class with catch-all segment anywhere in the URL
I'd like to have URLs that are even shorter than /{Controller}/{Action}/{Id}.
For example, I'd like {Controller}/{Id}, where {Id} is a string.
This would allow for simple paths, e.g. Users/Username, Pages/Pagename, News/Newsname. I like this better than requiring the /Details action in the URL (Users/Details/Username), which is less elegant for the end-user.
I can easily make this work by setting up custom routes for any controller that I want this level of simplicity for. However, this causes headaches when it comes to implementing other actions, such as {Controller}/{Action}, where {Action} = 'Create', since, in this case the string {Action} conflicts with the string {Id}.
My question: how can I have 'reserved' words, so that if the URL is /News/Create, it is treated as an action, but if the URL is anything else, e.g. /News/A-gorilla-ate-my-thesis, then it is treated as an Id.
I'm hoping I can define this when setting up my routes?
Update:
Using Ben Griswold's answer, I have updated the default ASP.NET MVC routes to be:
routes.MapRoute(
"CreateRoute", // route name
"{controller}/Create", // url with parameters
new { action = "Create" } // parameter defaults
);
routes.MapRoute(
"DetailsRoute", // route name
"{controller}/{id}", // url with parameters
new { action = "Details" } // parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
This works a charm and means, by default, the details pages will use the simplified URL, but I will still be able to target a specific action if I want to (update/delete/details).
Of course, you will need to disallow the reserved "Create" word as an Id, otherwise a user may try to create an article, for example, with the name "Create", which can never be accessed.
This is really nice. If anyone sees that there is something wrong with this approach, chime in, but I like it so far.
I think you're left with creating a route for each reserved word. For example,
routes.MapRoute("CreateRoute",
"{controller}/Create",
new { action = "Create" }
);
would handle /News/Create, /Users/Create, etc. As long as this route is listed before your other custom route, I think you're covered.
I imagine you will need addition routes for various CRUD operations which will follow a similar pattern.
I tried to use the solution explained at http://weblogs.asp.net/paulomorgado/archive/2010/01/31/web-site-globalization-with-asp-net-routing.aspx to localize my application using the language parameter in my routes.
Here's the code I have in my Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("en", new Route("en/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("en-US"))));
routes.Add("fa", new Route("fa/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("fa-IR"))));
routes.MapRoute(
"AdminHome",
"{language}/admin",
new { controller = "Admin", action = "Index" }
);
}
But when I point my browser to /en/admin or /fa/admin I receive a 404 error message.
I tried this one too:
routes.MapRoute(
"AdminHome",
"admin",
new { controller = "Admin", action = "Index" }
);
But still a 404 error for /en/admin - (in this case "/admin" works.)
Any idea?
I have a very similar route pattern in my own MVC site.
routes.MapRoute(
"BlogSpecific", // Route name
"{blogSubFolder}/{controller}/{action}", // URL with parameters
new { blogSubFolder = "", controller = "", action = "Index" } // Parameter defaults
);
The two main differences that I can see are that I specify the {action} in my route, and I also call out the first route param as a parameter in my object ("blogSubFolder = "",").
Now I just did some testing, and I found the same behavior that you are seeing, I take out the {action} out of my route and I get a 404. But if I specify the action everything works out.
Ok, so I created a new project, with the default route, and I don't have to specify the action, it defaults to Index just like I'd expect it to. I then add a new route where I specify the controller {language}/Foo/{action}, and I continue to get errors if I don't include the index in my url. Long story short, As near as I can tell if your route has a variable that precedes the controller you have to specify the action in your url.