I'm learning asp.net MVC and confused by this part.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Every article, including What is routes.IgnoreRoute("{resource}.axd/{*pathInfo}"), says the same thing, "this code is to ignore requests to axd files."
If I change the code to
routes.IgnoreRoute("{x}.axd/{*y}");
does it still work?
Do the things in curly brackets matter?
Yes, routes.IgnoreRoute("{x}.axd/{*y}") will still work.
Placeholder - {placeholderName}
The values within curly brackets are known as placeholders. These are simply variables and can be named whatever you want. When evaluating incoming URLs, the names don't matter at all. But when generating URLs or working out action method parameters or model property values, those names must match.
In the case of IgnoreRoute, there are no URLs generated, so those names are basically syntactic surgar.
Catch-All Placeholder - {*placeholderName}
The asterisk * indicates a catch-all placeholder. It is basically saying "match the URL, even if the rest of the segments from here to the end of the URL doesn't match the incoming URL".
Forward Slash - /
When using a catch-all placeholder as in this example, it indicates 1 or more optional segments. Since these segments are optional, so too is the right-most /. This is the same behavior when using the Default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
When the right-most segment is optional and it is not supplied in the URL (Home/About), this makes the right-most slash / optional as well. If the next right-most segment is also optional and omitted, the next right-most / is also optional. This explains why the default route matches the home page / instead of requiring // in order to match.
This behavior is special and only applicable to /. If you have placeholders with a different delimiter, such as {foo}-{bar} and bar is marked UrlParameter.Optional, - is still required (in fact, {bar} is required as well). /1-2 matches, /1- and /1 do not match.
Query String - ?key=value&key2=value2
Query string parameters are ignored completely when matching incoming routes. The reason why query string values are provided to MVC's ModelBinder and as action method parameters are because they are processed by value providers later on in the request.
On the other hand, when generating URLs (such as for ActionLink), any left over non-matching route values that are supplied (either in the request or directly) are added to the end of the generated URL as query string parameters.
#Html.ActionLink("Link", "Home", "About", new { key = "value", key2 = "value2" }, null)
Assuming the Default route, this ActionLink will generate the URL
/Home/About?key=value&key2=value2
The 'things' in the curly brackets matter when the route is mapped onto an Action (=method) in a Controller (=class). The names specified between the curly brackets map onto the action's parameters like so:
Account/{action}/{id}/{timestamp}
Will parameterise the following function:
public IActionResult Home(int id, DateTime timestamp) { ...
The asterisk * indicates that zero-or-many (like in RegEx) path segments can occur after that. For example /a/b/c/d/e/....
Related
I have the following code in my controller class
[Route("movies/released/{{year}}/{{month:regex(\\d{2}):range(1,12)}}")]
public ActionResult ByReleaseDate(int year, int month)
{
return Content(year + "/" + month);
}
And have the following code in the startup class
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "MoviesByReleaseDate",
pattern: "{controller=Movies}/{action=ByReleaseDate}/{year}/{month}") ;
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
I am trying to do attribute routing where as when I type in the URL
https://localhost:44334/movies/released/2022/06
it should just display 2022/06 on page. But instead its throwing 404 "not found" error. What am I doing wrong here? I'm using .NET Core 5.0
Use single curly brace instead of dual curly braces for each parameter.
While for the regex, you need dual curly braces \\d{{2}} otherwise you will get the exception
[Route("movies/released/{year}/{month:regex(\\d{{2}}):range(1, 12)}")]
You may also provide `:int` to each `year` and `month` in the route.
The :int portion of the template constrains the id route values to strings that can be converted to an integer.
Correction:
If you see the GetInt2Product sample,
The GetInt2Product action contains {id} in the template, but doesn't constrain id to values that can be converted to an integer. A GET request to /api/test2/int2/abc:
Matches this route.
Model binding fails to convert abc to an integer. The id parameter of the method is integer.
Returns a 400 Bad Request because model binding failed to convertabc to an integer.
It will automatically to convert to parameter type without mentioning in the route, and handling the scenario when failed to convert
Attribute routing with Http verb attributes
I would like my MVC website to be able to route the following URLs:
http://www.example.com/{userId}
http://www.example.com/{userId}/enroll
Note: userId is a string of letters, numbers and/or hyphens.
I realize this is problematic because the URL does not specify a controller name. However, it should be possible in theory. I can use reflection to get a list of all the controllers in my application. If {userId} does not match any of those controllers, then it should be redirected to a particular controller/action.
My first question is how would you specify a map like this? I can specify a string value, I can even specify a regular expression. But how can I filter based on whether or not it matches a list of strings?
Beyond that, just wondered if anyone else has thought of doing things and if they have any other creative ideas on how I might accomplish it.
If I understand your question right:
routes.MapRoute(
name: "Default",
url: "{id}/{action}",
defaults: new { controller = "Custom", action = "Index" },
constraint: new { id = new MyRouteConstraint()});
In custom MyRouteConstraint: IRouteConstraint you can match your parameter with list of strings
In ASP.NET Core, I want to generate some kind of SEO-URLs, that contains the id of an entry and also his title.
Example: example.com/News/ViewNews/123-test-news
In Startup.cs I defined the following Route
routes.MapRoute(
name: "SeoNews",
template: "{controller=News}/{action=ViewNews}/{id:int}-{title}"
);
In the News-Controller, the following Action exists
public IActionResult ViewNews(int id, string title) { }
The breakpoint inside gave me empty values, so id = 0 and title = null.
Calling
Example: example.com/News/ViewNews/123
Gave me id = 123, but empty title cause it's not present in the URL. Whats wrong with my Rewrite-Route?
Other Routes I've defined after my custom route:
routes.MapRoute(
name: "AreaRoute",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
routes.MapRoute(
name: "DefaultMVCRoute",
template: "{controller=Dashboard}/{action=Index}/{id?}"
);
EDIT
I tried replacing the hyphen with a slash like SO do, so the routing template is {controller=News}/{action=ViewNews}/{id:int}/{title}. That works perfectly.. Can't understand why. I already tried to escape the hyphen, cause it may conflicts as special char from regular expressions, which doesn't work too.
Using a slash as delimitter is a suiteable workaround for me. But I'm interested to know what's special on this char, that it doesn't work, so I renamed the question title.
I guess you mean slug instead of title? Title would imply that you want to use it as parameter. A slug is only there for the sake of having a more describable URL, but isn't actually used in the real routing.
This case is very well documented in the ASP.NET Core documentation already in the "Routing in ASP.NET Core" article.
You can use the * character as a prefix to a route parameter to bind to the rest of the URI - this is called a catch-all parameter. For example, blog/{*slug} would match any URI that started with /blog and had any value following it (which would be assigned to the slug route value). Catch-all parameters can also match the empty string.
routes.MapRoute(
name: "SeoNews",
template: "{controller=News}/{action=ViewNews}/{id:int}/{*slug}"
);
One reason why {controller=News}/{action=ViewNews}/{id:int}-{title} doesn't work is that title is not optional. Second is it tries to match the whole pattern between the slashes, so you can just have one parameter per segment.
You could also try this, but I doubt it will work
routes.MapRoute(
name: "SeoNews",
template: "{controller=News}/{action=ViewNews}/{id:int}{*slug}"
);
I want to create a route that uses params as part of the URL.
The problem I am having is when I create the default, I cannot use params as its a reserved c# word.
Wondering if there is a way around this. (see the use of params in route and when used as the default I get a compiler error: Expression expected)
Here is sample code:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{db}/{proc}/{params}",
defaults: new { controller = "Data", action = "Index", db = UrlParameter.Optional, proc = UrlParameter.Optional, params = UrlParameter.Optional}
);
You can use C# keywords by prepending # symbol to them, as MSDN suggests.
In your case, use #params instead of params.
To quote MSDN
Keywords are predefined, reserved identifiers that have special meanings to the compiler. They cannot be used as identifiers in your program unless they include # as a prefix. For example, #if is a valid identifier but if is not because if is a keyword.
Is there any particular reason you need this variable to be called params?
So I have two URLs that I need separate routes for:
/find-your-new-home/155-detroit/1234-some-value
and
/find-your-new-home/155-detroit/5555g-some-other-flipping-value
The route I have in place to handle #1 is:
routes.MapRoute(
"FindYourNewHomeCommunity",
"find-your-new-home/{market}/{community}.html",
new { controller = "Community", action = "Detail" }
);
I have an action filter on the "Detail" method that splits "155" from "detroit" and also splits "1234" from "some-flipping-value" and passes just the ID's to the action method (the id's are all that matter, the text values are inconsequential).
Now, the second route is almost exactly the same EXCEPT that there is a "g" after the "5555" value. This "g" indicates that it should call another action method ("CommunityGroup") on the Community Controller.
My question is: how do I configure two routes to handle these separately? I tried:
routes.MapRoute(
"FindYourNewHomeCommunityGroup",
"find-your-new-home/{market}/{communityId}g-{community}.html",
new { controller = "Community", action = "CommunityGroup" }
);
That doesn't work however, for two reasons:
1) Both URLs end up matching both routes as proven by Phil Haack's RouteDebugger.
2) Because of greedy matching (and this is why I used the text "flipping-value" in the sample URL), communityId ends up containing "5555-some-other-flippin" since it matches to the last occurrence of "g-" in the URL, which happens to be in the "flipping-value" text.
So my question is, how do I get a different action method to fire for these URLs?
EDIT: Just to clarify, these URLs are pre-defined by some other constraints I'm working in and cannot be changed. They have to follow exactly this format.
You could try creating a route constraint so that the "communityId" part of the route would only match numeric characters, eg:
routes.MapRoute(
"FindYourNewHomeCommunityGroup",
"find-your-new-home/{market}/{communityId}g-{community}.html",
new { controller = "Community", action = "CommunityGroup" },
new { communityId = #"\d+" }
);
If you can generate a list of communities, then you can write a custom regex constraint that will look up the communities at runtime and find them for you. It will then construct the constraint to only catch routes that match what you're looking for for the communities. One potential problem with it is that it only works on Application_Start. It'd be nice to change it dynamically. You could probably do it by updating the RouteTable.Routes at runtime when there are changes, but that is hacky.
Anyway, I ended up answering another Stack Overflow question that covered the same ground, here's the answer I wrote there.