How to change parameter name in controller in MVC4? - c#

I have a Controller like following:
public class PostsController : Controller
{
public ActionResult Index(int CategoryID)
{
return Content(CategoryID.ToString());
}
}
And my router is:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I wants add a MapRoute into RegisterRoutes like this:
routes.MapRoute(
name: "Posts",
url: "Posts/{action}/{CategoryID}",
defaults: new { controller = "Posts", action = "Index", CategoryID = UrlParameter.Optional }
);
I go to /Posts/Index/1 url but I give following error:
The parameters dictionary contains a null entry for parameter 'CategoryID' of non-nullable type 'System.Int32'
Note: in controller if I change CategoryID to id problem solved, and it worked!

As #Davide Icardi said, you need to place your new route above the default route.
Routes are evaluated from first to last; the first one to match will be used. Placing the route specific to the Posts controller at the top of the list will guarantee that it is matched before the default route.

Related

Returning a 404 when the URL does not contain Index

I am having trouble with routing in MVC. I have created a controller for my contact page, but unless I specify the route as /contact/index it will return a 404. I cannot see why it can't find the View with just /contact in the URL. My RouteConfig looks fine to me.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "T",
url: "T/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Holding",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Holding", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
The only reason I can see it not finding its View is because of the new Route I have configured to display a site holding page. Interestingly /t does display the 'demo' homepage, so I can't see why it doesn't like just /contact.
This S.O article told me that I could fix the problem by giving it its own MapRoute but I shouldn't have to do all that?
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Holding()
{
return View();
}
}
public class ContactController : Controller
{
// GET: Contact
public ActionResult Index()
{
return View();
}
}
It must be something silly, but I can't work it out.
You have route conflicts
/contact would match
routes.MapRoute(
name: "Holding",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Holding", id = UrlParameter.Optional }
);
But since contact controller has no Holding action you will get a 404 Not Found
And since it matched the Holding route it wont go on to the next Default route as first match wins.
The added route is too general so it will get a lot of false matches.
Based on the controllers shown, the added route is not needed. the holding path would still match the default route template. So it can actually be removed altogether.

I cannot figure out RouteConfig.cs file

I am very new to MVC and trying to build my first website. I couldnt set my RouteConfig file properly. I have 2 rules that apply to different ActionResults. But, only one of them works properly. if GetProducts is above the GetProductByCode, then GetProducts works. If GetProductByCode is above the GetProducts, then GetProductByCode works. What am I doing wrong?
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "GetProducts",
url: "{controller}/{action}/{PageNo}",
defaults: new { controller = "Home", action = "GetProducts", PageNo = UrlParameter.Optional }
);
routes.MapRoute(
name: "GetProductByCode",
url: "{controller}/{action}/{ProductCode}",
defaults: new { controller = "Home", action = "GetProductByCode", ProductCode = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
My Solution is like below
routes.MapRoute(
name: "GetProducts",
url: "{controller}/GetProducts/{PageNo}",
defaults: new { controller = "Home", action = "GetProducts", PageNo = UrlParameter.Optional }
);
routes.MapRoute(
name: "GetProductByCode",
url: "{controller}/GetProductByCode/{ProductCode}",
defaults: new { controller = "Home", action = "GetProductByCode", ProductCode = UrlParameter.Optional }
);
If you look at the default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Then think of the portion after url: as a format or pattern:
{controller}
{action}
{id}
Your 3 URLs Home/GetProducts, Home/GetProductsByCode and Home/Index all match this pattern.
With the {action} portion being GetProducts, GetProductsByCode and Index respectively.
You would need to leverage routing if you wanted to map the parameter to a variables called PageNo or ProductCode in the Action, but in general you don't need routes for each possible combination. If your parameters in these actions are id then it will just work without you needing to create routes for each.
E.g.
public ActionResult GetProducts(int id)
{
// stuff
}
public ActionResult GetProductsByCode(string id)
{
// stuff
}
To have the parameter names, specify the controller and action explicitly:
routes.MapRoute(
name: "GetProducts",
url: "Home/GetProducts/{PageNo}",
defaults: new { controller = "Home", action = "GetProducts", PageNo = UrlParameter.Optional }
);
routes.MapRoute(
name: "GetProductByCode",
url: "Home/GetProductsByCode/{ProductCode}",
defaults: new { controller = "Home", action = "GetProductByCode", ProductCode = UrlParameter.Optional }
);
And
public ActionResult GetProducts(int PageNo)
{
// stuff
}
public ActionResult GetProductsByCode(string ProductCode)
{
// stuff
}
But in general, only define custom routes that differ from the normal {controller}/{action}/{id} pattern.
The default section of MapRoute means that if it can't find a controller and action that exists in your code base use these instead. It's a fallback, not the functionality driver.
All 3 routes are identical in that they contain 3 segments (the controller name, action name and an optional parameter) and which ever of the 3 are placed first will always be hit.
If you want GetProducts to be hit the you could modify the definition to
routes.MapRoute(
name: "GetProducts",
url: "Home/GetProducts/{PageNo}",
defaults: new { controller = "Home", action = "GetProducts", PageNo = UrlParameter.Optional }
);
although there seems no real point. If you just changed the name of the parameter in the GetProducts() and GetProductByCode() to id then the only route definition you require is the Default
There is not way for asp.net to understand if the last parameter is /{ProductCode}", or {ProductCode}", since the action is the same - so your URL looks the same,
Therefor only the first match is taken, a solution would be using a full query string since the /{id} is just a shorthand for id=5 in the query string

MVC Routing issue: null entry

I have this Controller:
public class TestController : Controller
{
// GET: Test
public ActionResult Index()
{
return View();
}
public ActionResult Edit(int accessLevel)
{
return View();
}
}
Set up in RouteConfig.cs as:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Test Edit",
url: "Test/Edit/{accessLevel}",
defaults: new { controller = "Test", action = "Edit", accessLevel = UrlParameter.Optional }
);
If I go to this URL:
http://localhost:35689/Test/Edit/2
I get this error:
The parameters dictionary contains a null entry for parameter
'accessLevel' of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Edit(Int32)' in
'MyProject.Mvc.Client.Controllers.TestController'. An optional
parameter must be a reference type, a nullable type, or be declared as
an optional parameter. Parameter name: parameters
Any idea why that is? I would think that I'm providing the right datatype with /2.
The specific route definition should be defined before the generic default one.The order of route definitions really matters.
routes.MapRoute(
name: "Test Edit",
url: "Test/Edit/{accessLevel}",
defaults: new { controller = "Test", action = "Edit",
accessLevel = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
If you keep the other order like what you have (Generic-default first ,specific one later), When a request comes for Test/Edit/2 It will be matched to the generic route definition because Test is a valid controller and Edit is a valid action method name and 2 could be a valid param value for Id param.
Since the request got a valid route definition to match to it's url pattern, It will never be evaluated against other route definitions defined below the first one.
Keep all specific route definitions first and have the generic-default one as the very last one.
Or You may use attribute routing to define this route pattern in the Test controller.To enable attribute routing, you can call the MapMvcAttributeRoutes method in the RegisterRoutes method of RouteConfig.cs. You will still keep the default route definition there.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
and in your TestController
[Route("Test/Edit/{id?}")]
public ActionResult Edit(int? id)
{
//check id value and return something
}
Also, there is no point in defining a custom route if it matches with the generic default route definition. In your case, Even if you do not define the custom route ,Test/Edit/2 will go to the Edit action method of TestController as the request matches the default route definition.
People usually use these custom route definition to create nice url patterns like
[Route("Product/{id}/{name}")]
public ActionResult View(int id,string name)
{
//check id value and return something
}
This route definition will match the request Product/34/seo-friendly-name. Take a look at the URL of this question and you will understand what i am explaining here.
Switch the routes in RoutesConfig.cs. They should go from the most specific to general.
Your Default route is catching this one.
Please interchange your Route in RouteConfig becuase Order of routes are really matters a lot.

MVC4 Route as /{controler}-{id} instead of /{controler}/{id}

During my work I must solve one URL rewriting problem. I have to use URLs as a template:
{controller}/{action}-{id}
instead of
{controller}/{action}/{id}
so the processed url should look like:
myController/myAction-128
where 128 is a parameter
My route map:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "NewRoute", // Route name
url: "{controller}/{action}-{id}/{extId}", // URL with parameters
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, extId = UrlParameter.Optional } // Parameter defaults
);
}
Controller code:
[HttpGet]
public ActionResult DocumentDetails(int? id)
{
if (doc.HasValue)
{
...
}
}
This route doesn't provide any successful results. I still have 404 Errors. When I use / instead of "-" everything is ok, but my JS View environment won't work.
Is there something I could do to fix this? All help will be appreciated, thanks.
Routes are evaluated in the same order as you defined them, so make sure you respect the same order. Also adding a constraint for the id (as being a number for example) would help the routing engine disambiguate your routes. Not to mention that in your example you have made the id token optional which of course is not possible, only the last part of a route can be optional.
So:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "NewRoute",
url: "{controller}/{action}-{id}/{extId}",
defaults: new { controller = "Home", action = "Index", extId = UrlParameter.Optional },
new { id = #"\d+" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I'm thinking your request is being processes by the first routing rule and then action-id is considered as a whole action and not being found.
Set your NewRoute before the Default route. Just move the code up.

URL Parameter Not Recognized in Controller

I'm learning MVC 4, and it's my understanding that going to this URL should pass an int of 44 to the Edit() method of the controller. Indeed, when I go here:
http://localhost:51921/TrackerJob/Edit/44
... this method gets invoked:
public ActionResult Edit(int trackerJobId = -1)
{
Debug.WriteLine(trackerJobId);
}
... but the parameter is always -1. I had this working in a different project, but for some reason it's always -1 in this project. I don't see a difference between the two projects that would cause one to work and this one to fail. If I change the method signature to this:
public ActionResult Edit(int trackerJobId)
{
Debug.WriteLine(trackerJobId);
}
I get an error:
The parameters dictionary contains a null entry for parameter 'trackerJobId' of non-nullable type 'System.Int32'
Any ideas? I'm not sure what to check...
Edit - Including routes, by request*
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
If you want to use the default routing then just make sure your parameter is called id.
Otherwise you could add a new route like this:
routes.MapRoute(
name: "TrackerJob",
url: "{controller}/{action}/{jobtrackerid}",
defaults: new { controller = "TrackerJob", action = "Index", id = UrlParameter.Optional }
);
Make sure you add this route BEFORE the default route. The order of routes is very important!
Only you know if the trackerJobId is optional or not.
Note that if you want something more fancy you can tweak the routes to produce what you want.
e.g. If you want URLs like http://localhost:51921/TJ-E-44 for editing then your route would look like this:
routes.MapRoute(
name: "TrackerJobEdit",
url: "TJ-E-{jobtrackerid}",
defaults: new { controller = "TrackerJob", action = "Edit", id = UrlParameter.Optional }
);
I'm sure you get the idea.

Categories