This has probably been asked already - if so sorry! I couldn't find it.
I am unsure as to how asp is able to decide when to use a query string and "normal looking paths" (Embedded values)
Take this for example:
routes.MapRoute(
"SomePage",
"Net/Fix/{value}",
new { controller = "Net", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
I don't know how to explain - I will try.. If I am wrong please explain it
Question 1.
Is the first argument in mapRoute so that we can specify which routing we want to take place when using hyperlinks?
Question 2.
What does the second argument do?
It appears as if the second argument gives you the option of specifying how the routing should occur as below: "Net/Fix/hello" or by specifying placeholders in the form of {controller}/{action}/{somevar}
Question 3:
I assume if nothing is used in question 2 scenario - this specifies default routing that should take place?
Question 4:
How does ASP.NET infer whether to use a query string or an embedded value..
Because for example when I decide to call my page
http:/localhost:portno/Net/Fix/hello
It dutifully prints hello.. but when I do this
http:/localhost:portno/Net/Index/hello
It doesn't work.. unless I do
http:/localhost:portno/Net/Index?value=hello..
Question is... why?!!!
I hope questions were clear.. I will reply to answers (if any later).
The first argument is a route name. Each route should have a unique name, and they can be used for creating links, to assure a link is based on a certain route. It's not important in your case of matching a route.
The second argument is a matching pattern. Literal values are shown in clear, and parameterized values inside curly braces. {}. The parameterized values are not just for specifying the location of a parameter, but also the name of it.
I'm not sure offhand why you would define a route without any matching pattern. Does such an overload of MapRoute() exist?
The reason you get the behavior you do with this url: http:/localhost:portno/Net/Index?value=hello It matches the second (the default) route, not the first.
However, look at the second route pattern:
"{controller}/{action}/{id}"
The controller is the first parameter, action is the second. So with your URL, that request is routed to the Net controller, Index action. the same as your first example.
Because the query string contains a value parameter, that still gets passed to the action method. And it just so happens your action method has a string parameter named value, so it works.
Related
In my ASP.NET Core 2.1 project, I've noticed that my Html.ActionLinks and Html.Actions, that do not have an id set, are automatically including the id from the current URL.
For example, given the following Html.ActionLink in a view:
<li>#Html.ActionLink("Apply Online", "Apply-Online", "Careers")</li>
If the URL of the current page is localhost:1234/careers/apply-online/53, then when I inspect the href of the above Html.ActionLink in my navigation, it includes the "53" in the generated URL (even though it wasn't set in my view).
I can prevent this from happening if I explicitly set the id to nothing, like this:
<li>#Html.ActionLink("Apply Online", "Apply-Online", "Careers", new { id = "" })</li>
However, is there a way to stop this across the board without having to update all my action links?
I'm getting weird behavior, where if two controllers have the same action name (such as Index) and the URL for one has an id, it adds the same id to all of the other actions with a matching name on the page.
Here's my routing in Startup.cs:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
UPDATE
According to the documentation, it appears to be AmbientValues that is causing this behavior:
The second parameter to the VirtualPathContext constructor is a
collection of ambient values. Ambient values provide convenience by
limiting the number of values a developer must specify within a
certain request context. The current route values of the current
request are considered ambient values for link generation. In an
ASP.NET Core MVC app if you are in the About action of the
HomeController, you don't need to specify the controller route value
to link to the Index action—the ambient value of Home is used.
Ambient values that don't match a parameter are ignored, and ambient
values are also ignored when an explicitly provided value overrides
it, going from left to right in the URL.
So this seems to explain why, I can prevent the id from being automatically set if I explicitly set it to { id = "" } in my Html.ActionLink.
My question is, is there a way to set id to not use ambient values by default, perhaps in my MapRoute? And only use the id if it is explicitly set to a value in an Action or ActionLink?
Have you tried TagHelpers ?
TagHelpers remplace HtmlHelpers, they are more readable and easy to use.
For example, with HtmlHelpers the links were written like that:
#Html.ActionLink("Apply Online", "Apply-Online", "Careers")
It's remplaced by...
<a asp-area="" asp-controller="Careers" asp-action="Apply-Online"
asp-route-id="53"
asp-route-toto="foo">
Apply Online
</a>
<!-- expected: /Careers/Apply-Online/53?toto=foo -->
Obviously, if you remove asp-route-id, "53" is removed and if you remove asp-route-toto, the query string is removed too.
I am currently on the page /Customer/Edit/13244.
When I use #Url.Action("Edit", "Customer") on the same page it returns me /Customer/Edit/13244, but I want it to return /Customer/Edit/.
Kindly tell me how to fix this issue.
This is a "feature" of MVC that many people find unnatural and was previously reported as a bug.
Microsoft's official response:
Ultimately if you want the most control over what gets generated for a URL there are a few options to consider:
Use named routes to ensure that only the route you want will get used to generate the URL (this is often a good practice, though it won't help in this particular scenario)
Specify all route parameters explicitly - even the values that you want to be empty. That is one way to solve this particular problem.
Instead of using Routing to generate the URLs, you can use Razor's ~/ syntax or call Url.Content("~/someurl") to ensure that no extra (or unexpected) processing will happen to the URL you're trying to generate.
Actually, this bug only rears its ugly head when you try to re-purpose an action method name. If you use a different action method name other than Edit in the case where it is not followed by id, this problem will magically disappear.
You will need to use (assuming your using the default route with id = UrlParameter.Optional
#Url.Action("Edit", "Customer", new { id = "" })
I have a problem with a small app i am writing. Now either my error is in my controller class or its in Routes. See images below.
Controller Class.
This is the default Route i have.
And this is the error i get when i run.
Image not very clear but it says:
The parameters dictionary contains a null entry for parameter 'playerId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Detail(Int32)' in 'GlobalUnited.WebUI.Controllers.PlayerController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
I read through some posts on here, one particularly:
Similar Link
What exactly does he mean when Daniel Renshaw says:
First, I would suggest you use MVC's automatic handling of parameters instead ofpulling them out of the Request yourself. Your controller action has an id parameter which seems to go ignored - use that and add others like it to get the input parameters.
Anyways, after reading that post, i changed my RouteConfig file to:
And Still i got this error. It says:
A route named 'DefaultApi' is already in the route collection. Route names must be unique.
Parameter name: name
I even tried to change my Detail action parameter to: Note the int? declaration
And i got this error after changing:
Is there something i could do to fix this, something less complicated??
All help will be appreciated, thanks.
Your parameter name playerID causes the problem here. ASP.NET MVC can only provide you a parameter named id using the default route. In your case, it cannot map the request to your action because playerID is not nullable or optional. Changing your parameter name to id will solve the problem.
public ActionResult Detail(int id)
You get the second error with route registration because you already have a route named "DefaultApi". You won't need this route if you change the parameter name(also it's registered elsewhere).
Third error is for trying to querying with the null value. ASP.NET MVC cannot map the value in URL to your parameter and you get the default value null. Since there isn't a row with a null value you get an empty sequence, then Single() method throws an exception.
As your parameter is called playerId you must pass it as a route value to the Detail action method and then check if it's null or not. This will take care of that last error you show in your question: Sequence contains no element.
Do this in the action method:
public ActionResult Detail(int? playerId)
{
if(playerId.HasValue)
{
var model = _dataSource.Players.Single(p => p.PlayerId == playerId);
return View(model);
}
// Handle the other possibility where playerId is NULL
}
For ASP.NET MVC to know how to do the correct parameter binding you must call the above action method this way, for example:
#Html.ActionLink("Player Details", "Detail", new { playerId = 1 });
Other than the duplicate route error, your errors have nothing to do with the DefaultApi route. You get a duplicate route because that route is configured in App_Start\WebApiConfig.cs
Your first problem is that you are telling MVC that you have a mandatory Route parameter called playerId, but you are not supplying this route parameter in the URL you're using. In order for this to work you would need to either alter your route to change id to playerId, or add a playerId querystring parameter to your url. If you alter the route, you would need a url like http://my.site/Player/Detail/1 (if you alter the route) or http://my.site/Player/Detail?playerId=1.
Another option is changing the parameter to public ActionResult Detail(int id) which would then use the existing default route that takes a single parameter called id and extracts it from the friendly url that ends in /1.
You could also make the method Detail(int? id), but then you would need to place a null guard around your linq query (because if you don't pass the ID on the URL it can't lookup a null record) so you would have to add this:
if (id.HasValue) {
// execute linq query
}
Alternatively, you could alter your query to return SingleOrDefault() rather than Single().
SUMMARY:
I'm reviewing the route mapping for a site I've recently begun work on. I've encountered a route that I'm not familiar with:
RouteTable.Routes.MapRoute(NamedRoutes.ROUTE_NAME, "urlSegment1/urlSegment2", new { });
Notice that the "default" parameter for MapRoute is an empty object. This is normally where I would specify my controller, action, and any parameters. I've been googling around and am finding that I'm not asking the right questions to produce the answer I'm looking for.
QUESTION:
How does MVC routing behave when the "defaults" parameter of MapRoute is an empty object? An answer would be great. Supporting docs would be even better.
EDIT:
The actual route being used is:
routes.MapRoute(NamedRoutes.BROWSE_MEN, "browse/Mens", new { } });
And the problem is occurring when generating the URL using:
Html.BeginForm("Add", "Signup", FormMethod.Post, new { id = "signup", enctype = "multipart/form-data" })
They are mapped as stings and in this case matches everything. The defaults are only there if one of the controller/action parameters are not supplied by the url. In this case no defaults are meaningless. It is actually catching everything string/string.
The route you have there is will match a request that contains exactly the two segments in the url ex. http://localhost/urlSegment1/urlSegment2 and returns a 404. I guess you have this route defined to avoid the following routes handle this request.
When you don't specify a controller, either as URL parameter (token) or default value, you should get an InvalidOperationException:
The matched route does not include a 'controller' route value, which
is required.
Same for action. That is for incoming request. For URL generation there's no requirement for controller or action.
You don't have to specify defaults.
So it works as if there were no default object specified
I've been having some problems getting redirects after login to work how I want. So I came up with the idea to store the current page in the viewbag and use that to redirect, so if the page is mydomain.com/debate/1 I end up with "/debate/1" stored in the viewbad but when I try to redirect its giving me this complaint
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult DebateDetails(Int32)' in 'PoliticalDebate.Controllers.DebateController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
However If I manually type in mydomain.com/Debate/1 it works as expected.
Is there some way to get Redirect to work how I want ?
Since I don't see any code, I can't comment on the way you are trying to do it (which isn't working). On future posts, please post your code. This is one way how you can redirect if you are simply redirecting to the default action on the controller:
return this.RedirectToAction("Index", new { id = debateDetailsID } );
Although it's very hard to tell what you are truly trying to do because you mention debate/1 yet the method being called is DebateDetails which doesn't match (unless you've changed the default routes, again I don't know, there's no code).
Update
According to your comment, you have an error in your MapRoute. Your MapRoute should look like:
routes.MapRoute("Debate Details",
"debate/{id}",
new { controller = "Debate",
action = "DebateDetails",
// this id value is missing
// so it's not being passed to the controller
id = UrlParameter.Optional } );
the answer is there in the complaint, in this particular case you're sending a parameter so, checkout if this is specified
your code must look like redirectToAction("nameOfAction", new {id = yourIdOnViewBag}