Im trying to set up an alternative route on my application...
routes.MapRoute(
"x", // Route name
"{controller}/{action}/{datetime}", // URL with parameters
new { controller = "Home", action = "Index", datetime = UrlParameter.Optional } // Parameter defaults
);
I have the action...
public ActionResult GetBlogsByMonth(string datetime)
{
if (datetime!= null)
{
IList<BlogModel> blogs = (IList<BlogModel>)manager.GetBlogsInMonth(DateTime.Parse(datetime)).ToList();
return View(blogs);
}
else
{
return View();
}
}
But when I put the debugger on the action the datetime is always null... :-(
Probably, your request has been caught by another route. Make sure to put your route at the top when you are registering them.
For example, if you are using this route with the default one, the default one will catch the request, not your custom route if you reference them in the following order:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"x", // Route name
"{controller}/{action}/{datetime}", // URL with parameters
new { controller = "Home", action = "Index", datetime = UrlParameter.Optional } // Parameter defaults
);
As for the solution, as #Darin suggested, you need to define a constraint because if you put your custom one in front, this time the default one will never be hit.
routes.MapRoute(
"x", // Route name
"{controller}/{action}/{datetime}", // URL with parameters
new { controller = "Home", action = "Index", datetime = UrlParameter.Optional }, // Parameter defaults
new { datetime = #"^(19|20)\d\d([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])$" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
The below URLs will be caught by your custom route:
/Poo/Bar/2011-11-31
/Poo/Bar/2011-01-04
/Poo/Bar/2011-01-04
You can change the RegEx for your needs.
When constructing a link to your action you can use a RouteLink instead of an ActionLink. With the RouteLink you can pass a named route name to force the right route is chosen to construct the link. For your example the link should look somehow like this:
#Html.RouteLink("Blog Posts...", "x", new { controller="Blog", action="GetBlogsByMonth" datetime = THEDATETIME })
Hint: You can use the DateTime type in your action as parameter instead of a String type so you can avoid the unnecessary call to DateTime.Parse
Related
I want users to be able to access the "/Linecard" page of my ASP.Net MVC site using "/Linecard" or "/Manufacturers" as the URL... so same controller, 2 different possible URLs.
I tried adding the following:
routes.MapRoute(
name: "Manufacturers",
url: "Manufacturers/{action}/{id}",
defaults: new { controller = "Linecard", action = "Index", id = UrlParameter.Optional }
);
Adding this after the "Default" route doesn't work at all and I get a 404 error when I go to "/Manufacturers". Putting it BEFORE "Default" works, but then only "/Manufacturers" shows up in the URL when I click menu links since it is the first match. I would like "/Linecard" to always show as the URL.
Any pointers? Is there a certain constraint I can use to accomplish this? Thanks!
I had the same problem when we moved to extension-less URLs. We needed to continue to support one route with extensions. I got around it by having my default route apply to everything except the old URL, then after that mapping one specifically for the exception
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// if controller specified does not match 'manufacturers' (case insensitive)
new { controller = "^((?i)(?!manufacturers).)*$" },
new string[] { "Namespace.Of.Controllers" }
);
routes.MapRoute(
"Manufacturers", // Route name
"Manufacturers/{action}/{id}", // URL with parameters
new { controller = "Linecard", action = "Index", id = UrlParameter.Optional },
new string[] { "Namespace.Of.Controllers" }
);
You could also set an order when mapping your routes with the defaults at the end like so
routes.MapRoute(
name: "Manufacturers",
url: "Manufacturers/{action}/{id}",
defaults: new { controller = "Linecard", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
I'm kind of new to MVC. I have a controller called PostItemsController in an area called CaseManagers with an action method called GetByUmi(int caseNumber):
[HttpGet]
public ViewResult ViewByUmi(int umi)
{
//implementation omitted
}
The routing configuration looks like this (not my work):
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
//ignore route for ico files
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?../Images/MauriceFavicon.ico(/.*)?" });
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?Images/MauriceFavicon.ico(/.*)?" });
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?Content/Images/MauriceFavicon.ico(/.*)?" });
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?/favicon.ico(/.*)?" });
//ignore javascript files routing
routes.IgnoreRoute("{file}.js");
//ignore routing for files ending .doc
routes.IgnoreRoute("{resource}.doc");
routes.MapRoute(
"CaseManagers", // Route name
"CaseManagers/PostItems/ViewByUmi/{id}", // URL with parameters
new { controller = "PostItems" } // Parameter defaults
);
//InvoicesLookUp route
routes.MapRoute(
"InvoicesLookUpShortDefault", // Route name
"InvoicesLookUp/{action}/{id}", // URL with parameters
new { controller = "InvoicesLookUp", action = "Index", area = "Home", id = UrlParameter.Optional } // Parameter defaults
,
null,
new[] { "MooseMvc.Areas.Accounts.Controllers" } // Parameter defaults
).DataTokens.Add("area", "Accounts");
//Invoices route
routes.MapRoute(
"InvoicesShortDefault", // Route name
"Invoices/{action}/{id}", // URL with parameters
new { controller = "Invoices", action = "Index", area = "Accounts", id = UrlParameter.Optional } // Parameter defaults
,
null,
new[] { "MooseMvc.Areas.Accounts.Controllers" } // Parameter defaults
).DataTokens.Add("area", "Accounts");
//administrating route
routes.MapRoute(
"AdministratorShortDefault", // Route name
"Administrator/{action}/{id}", // URL with parameters
new { controller = "Administrator", action = "Index", area = "Administrator", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
//add root route route
routes.MapRoute(
"Root",
"",
new { controller = "Home", action = "Index", id = "" }
);
When I try to call this method with the URL http://localhost:[portnumber]/CaseManagers/PostItems/ViewByUmi/1234 I get the following exception:
The parameters dictionary contains a null entry for parameter 'umi' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ViewResult ViewByUmi(Int32)' in 'MooseMvc.Areas.CaseManagers.Controllers.PostItemsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
I don't intend the ID parameter to be optional and I don't understand why MVC can't find the ID.
Can anyone tell me what I need to do?
EDIT:
Phil Haack's route tester is telling me that the following route is being mapped:
routes.MapRoute(
"CaseManagers", // Route name
"CaseManagers/PostItems/ViewByUmi/{id}", // URL with parameters
new { controller = "PostItems" } // Parameter defaults
);
But it is being mapped AFTER another route CaseManagers/{controller}/{action}/{id}. But this route isn't anywhere in the Global.asax file (take a look, it's reproduced in full above).
Any idea what's going on?
Method parameters in ASP.NET MVC match up 1-1 with route parameters. Since you have no routes that take in a route value named umi, no route will catch what you're trying to do.
You have one of two choices:
If you want the default route to handle that action, then change:
public ViewResult ViewByUmi(int umi)
{
//implementation omitted
}
to:
public ViewResult ViewByUmi(int id)
{
//implementation omitted
}
However, if you want to keep umi(because it has contextual meaning that makes that code easier to follow), then you want to add a route to explicitly deal with it:
//UMI route
routes.MapRoute(
"umi",
"/case/postitems/view/{umi}",
new { area = "CaseManager", controller = "PostItems", action = "ViewByUmi", umi = "" }
);
Turns out that Global.asax isn't the only place that routing happens. Each of the areas in this application has its AreaRegistration class. I added a new route to the top of this class to produce the following:
public class CaseManagersAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "CaseManagers";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"PostItems", // Route name
"CaseManagers/PostItems/ViewByUmi/{umi}", // URL with parameters
new { area = "CaseManagers", controller = "PostItems", action = "GetByUmi", umi = "{umi}" } // Parameter defaults
);
context.MapRoute(
"CaseManagers_default",
"CaseManagers/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
The routing debugger now tells me this is getting matched first. Now I just need to work out why I've got an error telling the the resource cannot be found...
You don't have a route for CaseManagers/PostItems/ViewByUmi/1234 and it would appear that it is taking ViewByUmi and try to convert it to an System.Int32 because it is falling into the Default route. If you create a Route for your CaseManagers you should no longer have this problem.
Use Phil Haacks' Route Debugger to help you out :o)
routes.MapRoute(
"CaseManagers", // Route name
"CaseManagers/PostItems/ViewByUmi/{id}", // URL with parameters
new { controller = "PostItems" } // Parameter defaults
);
I have this URL:
/controller/action/value
and this action:
public ActionResult Get(string configName,string addParams)
{
}
How do I set up my routing table to get the routing engine bind the value to the configName parameter for any action in the Config controller?
Well, first off, that is incomplete. You don't have a method name.
Secondly, this will already work with URLs of the format:
/controller/action?configName=foo&addparams=bar
Here's how to do it with pretty routes:
routes.MapRoute(
"YourMapping",
"{controller}/{action}/{configName}/{addParams}");
or
routes.MapRoute(
"YourMapping",
"{controller}/{configName}/{addParams}",
new {
controller = "YourController",
action = "YourAction"
},
new {
controller = "YourController" // Constraint
});
if you want to exclude the action from the URL.
You could add a new route above the default
routes.MapRoute(
"Config",
"config/{action}/{configName}/{addParams}",
new { controller = "Config", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Which will allow you to use the route /config/actionName/configName/addParamsValue. Your other routes should be unaffected by this.
routes.MapRoute(
"ValueMapping",
"config/{action}/{configName}/{addParams}",
new { controller = "Config", action = "Index", configName= UrlParameter.Optional, addParams = UrlParameter.Optional } // Parameter defaults);
Setting default Controller to Home, with a Default Action of Index
So the Url:
/config/get/configNameValue/AddParamValue
would match this Method:
public ActionResult Get(string configName,string addParams)
{
//Do Stuff
}
I'm interested to know how people handle the following situation.
Assume we have a DataField and each DataField can have unlimited number of DataValues
We have 2 controllers to handle the manipulation of these objects
DataFieldController
DataValueContoller
Now if we ever need to add a new DataValue we need to know the ID of the CustomDataField. The following URL would be used,
/CustomDataValue/Add/1
1 = DataField ID
However, because the ASp.Net MVC engine binds the parameter name to the model (IE in the case below. My DatValeu Object would have its ID replaced, when I am actually trying to pass through the FieldID)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Site", action = "Home", id = UrlParameter.Optional } // Parameter defaults
);
How can we handle this? Doing the following obviously will not work.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Site", action = "Home", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{fieldid}", // URL with parameters
new { controller = "Site", action = "Home", fieldid = UrlParameter.Optional } // Parameter defaults
);
I assume this is a common problem, I just cant find the obvious solution at the moment. It would be ok if the Signature was differant but both are /String/String/Int
==========================
How can these routes work then?
/DataValue/Add/{DataFieldID}
/DataValue/Edit/{ID}
/DataValue/List/{DataFieldID}
Must I add 3 routes?
Use constraints in routes like this:
routes.MapRoute(
"Default", // Route name
"CustomDataValue/{action}/{fieldid}", // URL with parameters
new { controller = "Site", action = "Home", fieldid = UrlParameter.Optional } // Parameter defaults
);
It makes sure only URLs starting with "CustomDataValue" calls this route. It's declared as a constant, different from the default route. Make sure these specified routes are declared before the default route. Since there are no restrictions, all URLs are matched to it.
Update
I guess you have to call DataValueController methods with URLs like http://domain.com/CustomDataValue/Add/23. If that's the case use the following route:
routes.MapRoute(
"CustomData", // Route name
"CustomDataValue/{action}/{fieldid}", // URL with parameters
new { controller = "DataValue", action = "List", fieldid = UrlParameter.Optional } // Parameter defaults
);
This will work if you have action methods in DataValueController named List/Add/Edit.
I have two pages in my simple MVC App with two defined routes:
routes.MapRoute(
"Results", // Route name
"Results/{id}", // URL with parameters
new { controller = "Results", action = "Index",
id = "" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Main", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
I needed to have the results page load with just a product ID such as this: [MyDomain....]/Results/12345. But also the main page does a POST (using JQuery) to the Results Controller for updates using this route: [MyDomain....]/Main/Update along with a data bag. This works fine when I only have the "Default" route. But when I added the other "Results" route, all the POST calls to update are failing. Any ideas what I'm doing wrong???
Thanks a lot.
I didn't try this out, but should accomplish what you need. Not sure if there may be a "better" way to accomplish it.
routes.MapRoute(
"Results", // Route name
"Results/{id}", // URL with parameters
new { controller = "Results", action = "Index", id = "" } // Parameter defaults
new { id = #"\d+" } // regex for id param - id must be a number
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Main", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);