I know you can pass in parameters via urls like .com/MyPage/?controlID=5 but how can you do it with something like .com/MyPage/5? Thus not requiring the variable name or a question mark.
You would define a custom route, or use the model binding to get the intended effect. In your case, the route would be something like:
routes.Add("someRoute",
"{controller}/{action}/{controlId}",
new { controller = "Home", action = "Index", controlId = UrlParameter.Optional }
);
public ActionResult Index(int? controlId)
{
}
Now, the only "gotcha" with this route is that if you also have the default route specified, these two routes will be in contention and the first one you have defined will win. If there is some form of differentiating value (say, that controlId always matches some kind of pattern), then you can always add a HttpRouteConstraint to the route to differentiate your new route from the default route.
Alternatively, you can rename the parameter on your action method, if you are still using the default route, to be id, and change your query string key to 'id':
public ActionResult Index(int? id)
{
// Do Stuff
}
Create a method in MyPageController:
public ActionResult Index (int id)
{
}
That will work with the default routes
Related
I have the following code in a razor page:
#Url.Action("ArticleDetails", "Information", new { slug = article.Slug })
The page url where this code is placed has the form of http://localhost/category/6/category-name where 6 is the ID of the category
In the InformationController I have the following actions:
[HttpGet("article/{id}/{slug}")]
public IActionResult ArticleDetails(int id, string slug)
{
// some code ...
return View(data);
}
[HttpGet("article/{slug}")]
public IActionResult ArticleDetails(string slug)
{
// some code ...
return View(data);
}
How can I reach URL of form article/article-slug because #Url.Action(...) that I have in the page always try to reach controller action with id even if ID is not supplied as an anonymous type.
Links take the form of article/6/article-slug instead I want them to be article/article-slug without removing action with id in the controller.
I have noticed that 6 is from the id of the category. Also if I delete the controller action with Id i get the correct format of URL.
When resolving the action you're linking to, the IUrlHelper instance is using the current value of id in your current route (http://localhost/category/6/category-name), which has a value of 6, as you stated in your OP. Because there exists an ArticleDetails action that takes both an id and a slug (which you provide explicitly), the ArticleDetails action that takes both of these parameters is selected.
In order to resolve this, there are a couple of options. The first option is to clear out the RouteData value once you've used it in the action invoked when reaching http://localhost/category/6/category-name. In order to do that, you can use the following code within said action:
RouteData.Values.Remove("id");
I'm not a fan of doing it this way, but it does work. IMO, a better approach would be to simply use different names for the id parameter: e.g. categoryId and articleId in the respective controllers. This both fixes your issue and makes the code more readable in the corresponding actions. Your ArticleDetails action would simply change to:
[HttpGet("article/{articleId}/{slug}")]
public IActionResult ArticleDetails(int articleId, string slug)
{
// some code ...
return View(data);
}
Routing can be finicky when trying to do overloads like this. Your best bet is to use named routes:
[HttpGet("article/{id}/{slug}", Name = "ArticleDetailsIdSlug")]
public IActionResult ArticleDetails(int id, string slug)
[HttpGet("article/{slug}", Name = "ArticleDetailsSlug")]
public IActionResult ArticleDetails(string slug)
Then, in your view:
#Url.RouteUrl("ArticleDetailsSlug", new { slug = article.Slug })
Now, the routing framework doesn't have to try to figure out which route you actually want (and guess incorrectly apparently), as you'd told it exactly which route to use.
Say I have a controller called User, and I have a Index ActionResult, and then a secondary ActionResult called SetUserInfo. Example below:
public ActionResult Index(int id)
{
var enviorment = Zen.Components.Environment.GetEnvironmentByID(id);
return View(settingsViewModal);
}
//Second action
public ActionResult SetSite(int id, int siteID)
{
var enviorment =
Zen.Components.Environment.GetEnvironmentByID(
new EnviornmentQuery() {EnviormentID = id, SiteID = siteID);
return View(enviorment.Site);
}
Since the url "Settings?id=1" fires the ActionResult "Index", can I get "Settings?id=1&siteID=133" to then let the controller know it has to trigger ActionResult "SetSite" based on the params it was given, or do I have to make them optional in the first ActionResult, OR am I thinking of this all wrong. The route mapping is what is taking me a minute to fully get. I know it can be called as follows "Settings/SetSite?id=1&siteID=133", but wondering if I can do my prior example? If so, is it a bad way to handle it, or not?
You can map a route for it:
routes.MapRoute(
name: "SetSiteRoute",
url: "{controller}/{action}/{id}/{siteID}",
defaults: new { controller = "Settings", action = "SetSite" }
// Important part ^^^^^^^
);
Make sure you put it above your default route so that it takes precedence (routes are processed in order).
This will allow: www.site.com/Settings/1/500 where 1 is the id, and 500 is the siteID.
Although not exactly the answer you may be looking for..
First off, you cant map your route that way. The route cannot contain the ? character, so although you could use an alternative way to call your route using something like
/Settings/{id}/{siteId}
Using the following
/Settings?id={id}&siteID={siteId}
Cant be mapped as a route. However you could easily add the site id property to your Index action such as.
public ActionResult Index(int id, int? siteID)
{
if (siteID.HasValue)
return SetSite(id, siteID);
return null;
}
//Second action
public ActionResult SetSite(int id, int siteID)
{
return null;
}
I know this isnt exactly what you are looking for but will achieve the same result without having to mess around with your routes and urls. (Note i am just returning null so it compiles).
Cheers.
If an action exists on a controller, does asp.net-mvc route to that action before attempting to process any custom mapped routes?
Example.
Say I have the following controller
public class ShopController : Controller
{
public ActionResult Shop(Category category)
{
// returns some result
}
public ActionResult CartItemCount()
{
// returns some result
}
}
And I have registered the following route in my route collection:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Shop",
"Shop/{category}",
new { controller = "Shop", action = "Shop", category = UrlParameter.Optional } // Parameter defaults
);
}
What would happen if I had the following URL?
http://www.example.com/Shop/CartItemCount
Edit
I confused myself by thinking that the name of the mapped route was related to how it was processed. That is not the case, when it comes to url matching the name of the route does not matter.
As it turns out I had another route defined just above the one I gave in the example. This route, though named differently, was getting matched. I didn't even think to check it because, as I said, I thought the name given to a route somehow impacted the matching.
The routes decide which action to use. If there are no routes defined, you wont hit an action even if it exists. Install RouteDebugger and fire off your url. It will tell you ALL routes that it matches and which one it has actually used.
I have created a controller called loginController.cs and i have created a view called login.aspx
How do I call that view from loginController.cs?
The ActionResult is always set to index and for neatness, I want to specify what view the controller uses when called rather than it always calling its default index?
Hope that makes sense.
You can customize pretty much everything in MVC routing - there is no particular restriction on how routes look like (only ordering is important), you can name actions differently from method names (via ActionName attribute), your can name views whatever you want (i.e. by returning particular view by name).
return View("login");
In the interest of actually answering the question.. you can add a route ABOVE your default route in Global.asax:
routes.MapRoute(
"SpecialLoginRoute",
"login/",
new { controller = "Login", action = "Login", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
..although, without properly thinking through what you're trying to achieve (that being.. changing what MVC does by default) you're bound to end up with lots and lots of messy routes.
Your return the view from your controller via your Action methods.
public class LoginController:Controller
{
public ActionResult Index()
{
return View();
//this method will return `~/Views/Login/Index.csthml/aspx` file
}
public ActionResult RecoverPassword()
{
return View();
//this method will return `~/Views/Login/RecoverPassword.csthml/aspx` file
}
}
If you need to return a different view (other than the action method name, you can explicitly mention it
public ActionResult FakeLogin()
{
return View("Login");
//this method will return `~/Views/Login/Login.csthml/aspx` file
}
If you want to return a view which exist in another controller folder, in ~/Views, you can use the full path
public ActionResult FakeLogin2()
{
return View("~/Views/Account/Signin");
//this method will return `~/Views/Account/Signin.csthml/aspx` file
}
I have multiple controllers with different actions (no "Index" actions). The actions I consider "default" actions, are named differently. I want to create default routes for their names and have the first available action (from my list of default actions) executed if only the controller name is provided in the route.
So, for example, I have the following actions which I want to consider default and want checked for their existence in a controller in this order:
List()
Draw()
ViewSingle()
The routing should somehow search for /{controller} and then take the first available action from the list above as default action, e.g.:
/ControllerA -> ControllerA.List()
/ControllerB -> ControllerB.Draw()
/ControllerC -> ControllerC.ViewSingle()
/ControllerD -> ControllerD.Draw()
/ControllerE -> ControllerE.List()
Is this possible? I tried creating additional Default actions like this but couldn't get it to work:
routes.MapRoute("Default1", "{controller}/{action}",
new { controller = UrlParameter.Optional, action = "List" }
routes.MapRoute("Default2", "{controller}/{action}",
new { controller = UrlParameter.Optional, action = "Draw" }
routes.MapRoute("Default3", "{controller}/{action}",
new { controller = UrlParameter.Optional, action = "ViewSingle" }
Help?
I think you got something wrong about the default route. Those controller and action parameters are there for convention. If URL has a controller name in it, it will route to suitable controller. Same thing is true for Index methods. It just provides a default value for that.They are already optional and that's why. I think you don't need routes here: You could try to place your default functions in Index methods and it would work.Just to be clear:
public class ControllerA:Controller
{
public ActionResult Index()
{
return List();
}
public ActionResult List()
{
//List function
}
}
public class ControllerB:Controller
{
public ActionResult Index()
{
return Draw();
}
public ActionResult Draw()
{
//Draw function
}
}
I think it would work. But if you want to go on with creating routes, you create each route like this:
routes.MapRoute("Default1", "ControllerA",
new { controller = ControllerA, action = "List" }
Notice that ControllerA is not in curly braces,it's a static text. So you create routes for each controller. Keep controller naming convention in mind.
I agree with Narsil. You should write a constant to url. -like controller name- 'cause MVC cannot resolve this routes and cannot figure out what you want to show.
eg.
routes.MapRoute(null, "ControllerA/{action}",
new { controller = "ControllerA", action = "List" }
routes.MapRoute(null, "ControllerB/{action}",
new { controller = "ControllerB", action = "Draw" }
routes.MapRoute(null, "ControllerC/{action}",
new { controller = "ControllerC", action = "ViewSingle" }