How routing works in ASP.NET MVC4 - c#

I'm developing an ASP.NET MVC4 application and I'm new. I have two MapRoutes for routing.
routes.MapRoute("Char",
"ABC/Alpha/{number1}",
defaults: new { Controller = "ABC", action = "Alpha" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id=UrlParameter.Optional }
);
I didn't assign any default value to the number1 in the MapRoutes. But I know it is mandatory because it is in the URL path.
If I didn't provide any parameter in the url (when I run the app), I should get an error but I didn't.
Eg.:
http://localhost:32066/ABC/alpha/value---- getting desired output.
http://localhost:32066/abc/alpha/ ---- expected an error but there is no error.
Can anyone help me?

It doesn't match the first route, but it matches the second route. So it gets the controller/action = ABC/Alpha, and Id is optional.

Kyle is right, the second route (which is the global default) matches the url which is valid hence you don't see an error. I am guessing you are following one of the official Microsoft training videos that simulates an error in the request due to bad url that does not honor the route path.
To simulate the error, remove the default route, compile the project and run. Only this time specify the second url you have listed you should see the error.
Hope this helps.

The Route path is checked from top to bottom,
so in the first case
{Controller}/{Action}/{ID}
is passed by you (mention the id is must here) so the First routepath works
but Magic you was expecting the second case to throw an error, and it should have because if
your Action is like
public ActionResult ActionName(int id)
{
//some Code
}
then for sure it would have thrown error like Action
"Cannot convert null to 'int' because it is a non-nullable value type"
and i'm Pretty sure your Action will be having either
public ActionResult(int? id)
{//Some Code here}
or
Public ActionResult()
{
//Some Code here
}

Related

Dynamic routing in ASP.NET Core 3.1 when part of the url is already another route

I'm trying to use DynamicRouteValueTransformer with IEndpointRouteBuilder.MapDynamicControllerRoute to dynamically map new routes. This is for pages that are added while the application is running.
I'm doing:
endpoints.MapDynamicControllerRoute<RouteValueTransformer>("{**slug}");
to catch all urls that don't already have a route assigned, so that RouteValueTransformer can try to find one when necessary. By putting a breakpoint inside the RouteValueTransformer.TransformAsync method, I can see when it runs. If you just type any nonsense url in, it will enter the method as expected. For a url that doesn't have a route assigned, but an earlier part of the url does, it won't enter the method, as it seems to think it already has a route, but then it obviously gives a 404 error.
For example, if after the above line I do
endpoints.MapControllerRoute(
name: "test",
pattern: "category/product1",
defaults: new { controller = "Home", action = "Index" });
and then try to browse to "category/product2", I think it should enter the TransformAsync method to try and find a route, but it doesn't, and it gives a 404. If you change "category" to something else, it will enter the method.
I'm not sure if this is expected behaviour, a bug, or if I'm using the "pattern" parameter wrong, or maybe {**slug} wrong?
To reproduce
It's very easy to reproduce. Create a new ASP.NET Core 3.1 web application. Add
app.UseEndpoints(endpoints =>
{
var defaults = new { controller = "Home", action = "Index" };
endpoints.MapControllerRoute(name: "test", pattern: "category/product1", defaults: defaults);
endpoints.MapDynamicControllerRoute<RouteValueTransformer>("{**slug}");
});
to the end of Startup.Configure,
services.AddSingleton<RouteValueTransformer>();
to Startup.ConfigureServices,
and
public class RouteValueTransformer : DynamicRouteValueTransformer
{
public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
return values;
}
}
and set a breakpoint on return values; to see when it runs.
What else I've tried
endpoints.MapDynamicControllerRoute<RouteValueTransformer>("{one}/{two}"); - also doesn't enter method.
Looking through documentation. There isn't much on DynamicRouteValueTransformer (yet) unfortunately so I've found it quite difficult.
Update
I'm starting to think that it is mostly likely a bug, so I've raised this Github issue
It was a bug and a fix is planned. See https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-3.1#conventional-routing-order

Routes are simultaneously valid, yet broken

I asked a previous question here, in which I attempted to use a blank URL to catch a default page.
After some more digging, and some trial and error, I stumbled upon the use of {*url} to catch the root URL. I also attempted to use a constraint to manage the "tidy" url that I want to use. My RouteConfig now looks like so:
routes.MapRoute(
name: "LoginRoute",
url: "{login}",
defaults: new { controller = "LoginController", action = "Index", id = UrlParameter.Optional },
constraints: new { login = "login" }
);
routes.MapRoute(
name: "Default",
url: "{*url}",
defaults: new { controller = "authController", action = "routingsuccess" }
);
However, neither of these routes result in a web page. Instead, they still result in 404. Curiously, however, Phil Haack's RouteDebugger reports that the URL I am using is valid, as demonstrated here:
To clarify, accessing the root url (in this case, localhost:3000) results in the same issue.
There is a valid controller, and a valid view behind it with the appropriate action. What could possibly be wrong?
You can just use the normal default routing.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "authController", action = "Index", id = UrlParameter.Optional }
);
This just mean, if a controller isn't passed in the url then it will use MainController. If an action isn't passed then it will use Index. This mean http://website.com will go to MainController action Index.
Maybe you are using [Authorise] attribute, or something else for authentication.
I will just make a guess : -
Your route is registred, and there is no problem in it. Problem occurs when you try to access it. There might be some kind of authorisation, like an [Authorise] attribute, that would block non access users to get to your route.
Or there might be something else, that would be causing your code to not be able to reach the ActionResult.
To confirm this, put a breakpoint in the constructor of the COntroller.Remove all attributes from the COntroller. If your debugger stops at the breakpoint in constructor, then the issue is not is registering the route, but access related.
Let me know if it helps.

C# MVC, Action Method doesn't read the parameter from QueryString

This must be a small thing, but I am not able to figure out the issue.
I am passing 2 parameters through Querystring, but my index method receives only one of them. Not sure what could be going wrong here.
Here's my Index method
public ActionResult Index(Guid? empGuid = null, Guid? empPriorGuid = null)
{
// do something
}
And here's my URL :
baseurl/?empGuid=7a3b9a5d-b7dd-4959-a1df-be35546d2db7&empPriorGuid=f530733e-ce8d-4bbf-8480-3551dce91337
My Index seems to receive the empPriorGuid but not the empGuid for some reason.
IF you are using Areas you need to define a route having 2 parameters
I was able to figure out the issue, it was just a strange case that something was wrong with the empGuid, when I used a different Guid it did work. Thank you all!
you can change route config as below :
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{empGuid?}/{empPriorGuid}");
the "?" in routing means optional
and call action as below :
#Html.Action("index", "home", new { empGuid =7a3b9a5d-b7dd-4959-a1df-be35546d2db7,empPriorGuid=7a3b9a5d-b7dd-4959-a1df-be35546d2db7})

Route variables received as action variables?

I've got the following route configuration:
routes.MapRoute(
name: "Default",
url: "{project}/{version}/{controller}/{action}",
defaults: new { controller = "Portal", action = "Index" }
);
The idea is to have two custom parameters in the URL, before anything else so an action filter I created can do a few useful things. That works well so far, but here's the problem.
Here's an action method with a parameter called version
public ActionResult SomeMethod(string version)
{
//Some logic has been performed using the custom route variable values before getting in here....
}
If I do the following call: http:/server/RouteValue1/RouteValue2/MyController/SomeMethod?version=1
The value of the version parameter of SomeMethod is RouteValue2.
Is there any way to tell the framework not to do that? Keep route variables seperate from action variables ?
No, you can't use the same parameter name in both the route config and as a query string. You'll need to rename one, i.e.:
http:/server/RouteValue1/RouteValue2/MyController/SomeMethod?ver=1

How to define this route in ASP.NET MVC?

I have a controller named Movie, with an action named ByYear, which takes the year as a parameter :
public ActionResult ByYear(int year)
{
ViewData["Title"] = string.Format("Movies released in {0}", year);
var repository = MvcApplication.GetRepository();
var movies = repository.Medias
.OfType<Movie>()
.Where(m => m.Year == year);
return View("Index", movies);
}
I'd like to access this action with the following URL : /Movie/ByYear/{year}, but the only valid route for this action is this : /Movie/ByYear?year={year}.
I tried to add new routes in my application's RegisterRoutes method, but I can't find a way to get the desired result...
Could anyone tell me how to achieve that ?
Note: this is actually very similar to this question, but no answer was accepted, and the highest voted answer makes no sense to me as I'm completely new to MVC...
Change the name of your parameter year to id and this will match the default route that MVC adds to your project.
So for further clarification, let's take a look at the default route added by ASP.NET MVC:
routes.MapRoute(
"default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
In this route you can see three tokens that are named specifically for controller, action, and the third token which is passed to the action is id. When a request comes into your application, ASP.NET MVC will analyze the routes that are currently mapped and try to find a method signature that matches them by using reflection against your controllers.
When it looks at your Movie controller, it sees an action called ByYear, however that method takes an integer called year, not id. This is why you end up with something like /Movie/ByYear?year={year} when you create an ActionLink for that particular Action. So to fix this, you have two options:
The first and most simple method to fix this is to just change the method signature for your Action to accept a parameter named id which is what I recommended above. This will work fine, but I can see where it might cause a little bit of confusion when you go back to that source later and wonder why you called that parameter id.
The second method is to add another route that matches that method signature. To do this, you should open your Global.asax and just add the following (untested, but should work):
routes.MapRoute(
"MoviesByYear",
"Movies/ByYear/{year}",
new { controller = "Movie", action = "ByYear" }
);
This route is hard-coded, yes, but it won't break the other routes in your system, and it will allow you to call the method parameter year.
EDIT 2: Another thing to note is that the routing engine will stop on the first route it finds that matches your request, so any custom routes like this should be added before the default route so you are sure they will be found.
OK, I just found out how to do it. I just had to create the new route before the default route... I didn't think the order had any significance
routes.MapRoute(
"MovieByYear", // Route name
"Movie/ByYear/{year}", // URL with parameters
new { controller = "Movie", action = "ByYear" } // Parameter defaults
);
EDIT: Isn't there a simpler way ? (not involving renaming the parameters). I'd like to be able to do something like that :
[Route("/Movie/ByYear/{year}")]
public ActionResult ByYear(int year)
{
...
Design considerations aside, if you did not want to rename the parameter, you could add something like the route below, which enforces having the year parameter
routes.MapRoute(
"MovieByYear", // Route name
"Movie/ByYear/{year}", // URL with parameters
new { controller = "Movie", action = "ByYear" },
new { year = #"\d+" } // Parameter defaults
);

Categories