I thought I had at least a bases for understanding routing in MVC after all the documentation I have read, only to fail at it when attempting to utilize it.
I have the following two routes declared in my Global.aspx
routes.MapRoute(
"", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Admin", action = "List", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
I have a AdminController that I have a few methods, one that is "List" method that renders a list of products to a "List" view.
I thought I understood how RedirectToAction works, and I added an "Add" method (see below) that adds a new Product and returns a RedirectToAction which I understood would be the proper way to redirect to the List action on the same "AdminController"
[HttpPost]
public ActionResult Add(Product product) {
if (_prodRepo.Add(product)) {
return RedirectToAction("List", "Admin");
}
return View("Add", product);
}
However, on the return of the "Add" it always attempts to route to the path website.com/Account/Login/ReturnUrl=%2f.
However, if I go to website.com/Admin it does render the list as I expect. But when using the RedirectToAction as in the example above, it attempts to go to the /Account/Login (Controller/action).
It was my understanding that the RedirectToAction("List", "Admin") would route to the "List" method on the AdminController controller and that I was using it as expected.
Can someone please help me understand the reasoning behind this. But also, could someone post some recommended articles for understanding the whole MVC routing including how the web.config works with routing.
Finally, it was also my understanding that route discovery by the framework is done in the order they are specified in your routes.MapRoute() declaration and stops at the first one that matches. Therefore, if the first one is listed as Controller = "Admin", Action = "List", I would expect by convention that this is the correct route it would first match and return.
Your routes need to be different (the url parameter) since the first route with a matching url will be used.
This will therefore work for you:
routes.MapRoute("Admin",
"admin/{action}/{id}",
new { controller = "Admin", action = "List", id = UrlParameter.Optional });
The defaults (third parameter in the method) are used if the parameters isn't found/specified in the uri.
As for your question regarding /Account/Login/ReturnUrl=%2f. The login redirections are handled by the MembershipProvider and not by the standard routing mechanism.
Related
I'm teaching myself MVC and have an issue with routing correctly
I have a controller named "ClipsController" and 1 view inside with the name "Index" (boring, i know)
I have my routeconfig file configured with the following route:
routes.MapRoute(
"Clips",
"Clips/{id}",
new { controller = "Clips", action = "Index", id = urlparameters.Optional }
);
which is Before the "Default" route. When i go to /Clips/ExampleID
it does hit the correct route, and does start the Index action in the Clips controller. What i'm having trouble with is the parameter 'ID' fails to pass through into the Index page, but i end up on the index action of the Clips controller, with the URL domain.my/Clips/ExampleID
I attempt to get the ID parameter with
httpcontext.current.request.querystring["id"]
which always returns a null. My actionresult in the controller is as follows:
public ActionResult Index(string id)
{
return view()
}
To reiterate, I'm not able to see the querystring id on the index view, even though the URL does the correct route, and the actionresult in the controller is to my kowledge correct. If i have done something critically wrong or you need more information please let me know, Thanks.
I attempt to get the ID parameter with
httpcontext.current.request.querystring["id"]
No, you don't have any query string parameters in this url:
http://domain.my/Clips/ExampleID
Query string parameters follow the ? character in an url. For example if you had the following url:
http://domain.my/Clips?id=ExampleID
then you could attempt to read the id query string parameter using your initial code.
With this url: http://domain.my/Clips/ExampleID you could query the id route value parameter. But using HttpContext.Current is absolutely the wrong way to do it. You should never use HttpContext.Current in an ASP.NET MVC application. Quite on the contrary, you can access this information everywhere where you have access to HttpContextBase (which is pretty much everywhere in the ASP.NET MVC application pipeline):
httpContext.Request.RequestContext.RouteData.Values["id"]
Long story short, if you needed to query the value of this parameter inside your controller action you would simply use the provided id argument:
public ActionResult Index(string id)
{
// Here the id argument will map to ExampleID
return view()
}
Also you probably don't need your custom route:
routes.MapRoute(
"Clips",
"Clips/{id}",
new { controller = "Clips", action = "Index", id = urlparameters.Optional }
);
That's completely redundant and it is already covered by the default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
So feel more than free to get rid of your custom route.
PLEASE MARK AS DUPLICATE - Its already answered here
Is it possible to have a parameter named action in MVC4?
Trying to do this results in the parameter returning me the name of the controller action rather than the parameter value.
/Somecontroller/Someaction?action=value
When I try to access the parameter action, I get "Someaction" as the result rather than "value".
Trying to bind the parameter to a different name doesn't help either.
public ActionResult Someaction([Bind(Prefix = "action")] String id)
Edit: I have not found 'Action'/'action' in reserved MVC keywords either.
I haven't tested this, but I would assume you could try changing the url of your default route to something other than action
routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
to
routes.MapRoute("Default", "{controller}/{cAction}/{id}",
new { controller = "Home", cAction = "Index", id = UrlParameter.Optional });
this should then allow you to use action as a parameter in your Action. I'm not certain why you'd want to do this, I would probably build a route url that accepted my action and built it as part of the url similar to this:
/SomeController/SomeAction/value where value is your "action" parameter.
EDIT based on comments:
I successfully created a route that goes to an aspx url'd route
routes.MapRoute(null, "third-party.aspx", new { controller = "Home", action = "Index" });
Obviously you can add whatever controller/action you want here and where you want to handle it, then you access that route via /third-party.aspx?action=value and it seemed to work for me
I would like to have some paramaters after my website's URl.
So, mysite.com/ThisIsMyStringMessage, would show this :
Hello, ThisIsMyStringMessage.
Of course, the view related to the action contains this :
Hello, #ViewBag.Message
And the HomeController :
public ActionResult Index(string message)
{
ViewBag.Message = message;
return View();
}
Edit related to comments :
#Zim, I'll have more than one controller.
#Jessee, So that's what I'm doing here, thank you, I didn't know really what I was doing.
With the default routing, ThisIsMyStringMessage will be picked up as the Controller, not as an Action method parameter. You will need a custom route to be able to do this. Try the following before the default route in Global.asax:
routes.MapRoute(
"RootWithParameter",
"{message}",
new { controller = "Home", action = "Index", message = UrlParameter.Optional }
);
Alternatively, access the method with mysite.com?message=ThisIsMyStringMessage
You will need to take a look at your RouteConfig.cs or Global.asax to setup a route with the parameter of message instead of id. Right now it probably looks like:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You would need to change the url parameter to something like {controller}/{action}/{message} for that to work.
Also, be careful with passing strings in the URL. ASP.NET (and some modern browsers) gives you many security protections to prevent people from doing nasty stuff like XSS, but it's probably not a habit you want to create.
I have an asp.net MVC project where i need to define some custom routes. Similar to what you see for posts on Wordpress where the route is of the form postid-postname:
12-i-am-post
I know how to do something like postid/postname:
12/i-am-post.
But how do I make a route that combines the two, such as:
mywebsite.com/12-postname-is-her
routes.MapRoute(
"PostRoute", // Route name
"{controller}/{id}-{postName}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, postName = UrlParameter.Optional } // Parameter defaults
);
public ActionResult Index(int id, string postName)
{
return View();
}
Should work for the following request http://localhost/Post/1-MyPostName
Since there are multiple hyphens you won't be able to use it as a delimiter/separator, unless you want it to always match the first occurrence. Your best bet is to do a catchall {*path} and parse the value in your controller.
I need to access a parameter without name.
Ex: I have a controller test, I want to get "data" in mysite.com/test/data. Without calling the action data.
I know how to do that passing it though Index action.
public ActionResult Index(string id)
That way I'd only need to type mysite.com/test/Index/data to get "data", but I don't want to have to type Index.
Does anyone know how to do it?
EDIT:
Thanks a lot to #andyc!
AndyC I used what you said and created a test. It worked =D
Now I can type mysite.com/something and if something is not an controller it redirects to my profile page.
This is what is working for me
routes.MapRoute(
"Profile",
"{id}",
new { Controller = "Profile", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Setup a custom route
In your global.asax file, put (Inside the RegisterRoutes method):
routes.MapRoute(
"MyShortRoute",
"view/{id}",
new { Controller = "test", action = "Index" }
);
The first parameter is a name, the second is the URL format and the last parameter is the default values (in this case if you leave id empty it'll default to id 0.
Notice that I don't use test/{id} as this would also match test/Edit, where edit is an action that you do not want passed as a parameter (I can't think of another way to avoid this, especially if you're using strings instead of ints for your parameters).
Remember to order your routes appropriately in your global.asax file. Put more specific routes before less specific routes. When the system is searching for a route to take, it does not attempt to find the most specific match but instead it starts from the top, and uses the first match it finds.
Therefore, this is bad:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "test", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Specific",
"test/{id}",
new { controller = "test", action = "Index", id = 0 }
);
In this example, if you browse to test/somevalue it will match the FIRST entry, which is not what you want, giving you the testcontroller and the somevalue action.
(As your adding a more specific route, you will want it near the top, and definitely before your default).