Looking at the help Attribute Routing in ASP.NET MVC 5 it is easy to see how to constraint a parameter as below:
[Route("edit/{promoId:int?}")]
public ActionResult Edit(int? promoId) { … }
So this route will only accept promoId with int values or empty.
Some valid URLs for this route would be:
/promotions/edit/5
/promotions/edit/
But how to set a RouteAttribute to accept "/promotions/edit/promoId=5"?
You try to set it in RouteConfig.cs in your App_Start folder by pointing that kind of URL to your action.
routes.MapRoute(
name: "Edit",
url: "{controller}/{action}/promoId={promoId}",
defaults: new { controller = "Promotions", action = "Edit", promoId = UrlParameter.Optional }
);
Actually, I think the url should be in this format:
/promotions/edit?promoId=5
Note the ?. It's the beginning of the query string marker.
It should be possible to do it this way:
[Route("edit")]
public ActionResult Edit([FromUri]int promoId)
{
...
}
Related
I am learning ASP.NET MVC and URL routing seems to be a very tough chapter in whole MVC. I read many blog post and many other SO questions but none of them helped the way to understand every aspect of routing.
I would like to have a URL likes www.sitename.com/controller/action/username/userid. How can I do it with MVC routing? A detailed answer to cover every aspect of it would be very helpful.
1. Using Traditional convention-based routing
Update your route registrations to include this new url pattern. Make sure you do this before registering the default route registration.
So in your RouteConfig file
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{userName}/{userId}",
defaults: new { controller = "Home", action = "Details" }
);
routes.MapRoute(
name: "Default2",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
This will route all the requests matching that url pattern to the corresponding controller's action method (with the 2 parameters). This means, If you have 2 controllers, both having an action method with userName and userId params, based on your controller & action method name in the request url, the corresponding method will be returned.
2. Attribute Routing
public class HomeController : Controller
{
[Route("Home/Details/{userName}/{userId}")]
public ActionResult Details(string userName,int userId)
{
return Content("Details : "+userName + userId);
}
}
Here we are registering the new route which says when the request url is Home/Details/{userName}/{userId}, return the response from Details action method of Home controller. ( This is very specific as we define specific controller name and action name)
This is an official article on ASP.NET MVC Attribute Routing which for most scenarios is sufficient. Sometimes in advanced scenarios you need to configure routes in the Global.asax/Bootstrapper.
Basically it would look like:
[RoutePrefix("Controller")]
public class Controller : Controller
{
[Route("action/{username}/{userid:int}")]
public ActionResult Action(string username, int userid){}
}
I did that from memory so it might not be exactly right, please refer to the documentation in that link and what's on MSDN.
I am trying to configure routing with both Conventional & Attribute based.
If I just use the default Conventional route included with MVC everything works. but if I add this Route attribute, I get a 404.
Here is the GET request URL: http://localhost:52386/Home/SimpleSearch?searchTerms=test&dateRange=0
Here is my RouteAttributes in Code:
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("SimpleSearch/{searchTerms}/{dateRange}/{page?}")]
[HttpGet]
public ActionResult SimpleSearch(string searchTerms, DateRangeEnum dateRange, int page = 1)
{
//Code here
}
}
Also the Route Config looks like this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
//Default
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
I don't see what is wrong with this RouteAttribute, but even if something is wrong with it, why doesnt it fall back onto the default Conventional Route and work?
With the attribute route definition, you explicitly specified the route pattern to be
Home/SimpleSearch/{searchTerms}/{dateRange}/{page?}
So you should try to access your action method with same url pattern.
This should work.
http://localhost:52386/Home/SimpleSearch/test/0
and Model binder will be able to map "test" to searchTerms parameter and 0 to dateRange parameter.
Your conventional (explicitly using querystring) will not work when you have an attribute route with a different pattern
I have a bunch of mostly-static pages (about 40),
like: order-form01.html, order-form02.html, orderform03.html etc..
Should each of them have its own Controller/Action, or is that possible to have one dynamic Controller/Action for all of them?
My Url should look like this: http://MyProject/GlobalController/IndividualView and for the above example: http://MyProject/OrderForm/order-form01, http://MyProject/OrderForm/order-form02 etc..
Thanks in advance.
Yes it's very easy AND you don't need a switch statement or any other redundant logic.
public class MyController
{
public ActionResult Page(string file)
{
return View(file);
}
}
The magic is in the Route Map:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// New MapRoute for your 40+ files..
routes.MapRoute(
"OrdeForm",
"OrderForm/{file}",
new { controller = "MyController", action = "Page", {file} = "" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
Additionally:
I pass the View name in the query string.
Is not required, but is supported. The following urls will work:
// Url Parameters
http://MyProject/OrderForm/order-form01
http://MyProject/OrderForm/order-form02
// Querystring Parameters
http://MyProject/OrderForm?file=order-form01
http://MyProject/OrderForm?file=order-form02
The only catch is that you need to rename your html files to cshtml and place them in the correct directory for the ViewEngine to find.
#Erik, I also bit of new to mvc . Could you please explain your route map as of how is it possible with default raute again and again
Routes are broken down into 3 values:
Controller
Action
Parameter(s)
At a bare minimum, the controller and action are required. Where the values come from is not dependent on the Url. For example, in the following Url and Map Route...
// Url
http://MyProject/
// MapRoute
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = "" }
);
// Controller named "Home" matches the default in the above route
// Method named "Index" matches the default in the above route
public class HomeController {
public ActionResult Index() {
return new EmptyResult();
}
}
... everything still works because we provided a default value for the controller and action.
Ok let's break down the URL you want:
http://MyProject/OrderForm/order-form01
http://MyProject/OrderForm/order-form02
http://MyProject/<identifier>/{parameter}
You have one identifier that tells me route (OrderForm) and one changing value that because it changes and you want one value, should be a parameter.
http://MyProject/<identifier>/{file}
The name of the parameter makes no difference as long as it matches the signature of the controller method:
http://MyProject/{Controller}/{file}
public class HomeController {
public ActionResult Index(string file) {
return new EmptyResult();
}
}
or
http://MyProject/{Controller}/{einstein}
public class HomeController {
public ActionResult Index(string einstein) {
return new EmptyResult();
}
}
I named the parameter file, because it tells me it's the parameter is a name of a file, whereas the name einstein has no inherent description so is a terrible name for a variable.
http://MyProject/{Controller}/{file}
// MapRoute
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = "" }
);
// Controller named "Home" matches the default in the above route
// Method named "Index" matches the default in the above route
public class HomeController {
public ActionResult Index() {
return new EmptyResult();
}
}
Now we only want this route to run when the identifier is OrderForm so we don't allow that to be a value, we hard code it.
url: "OrderForm/...
Next we have a value that keeps changing, so we to add url parameter:
url: "OrderForm/{file}"
Now we have an issue because we aren't allowing MVC to parse values from the url to populate Controller nor Action so we must supply them.
routes.MapRoute(
name: "",
url: "OrderForm/{file}",
defaults: new { controller = "Home", action = "Index", file = "" }
);
Here we've mapped the url http://MyProject/OrderForm/{file} to:
public class HomeController {
public ActionResult Index(string file) {
return new EmptyResult();
}
}
Now I would choose to to update the defaults to something more specific and descriptive:
routes.MapRoute(
name: "",
url: "OrderForm/{file}",
defaults: new { controller = "OrderForm", action = "Index", file = "" }
);
public class OrderFormController {
public ActionResult Index(string file) {
return new EmptyResult();
}
}
Hope that all makes sense.
After the question edited :my solution is, you can have one controller/action and it should call view (cshtml). Your querystring data should be pass to view as of viewbag variable and partial views should be called acording to the viewbag variable. noo need of editing routing table even(if you are willing to pass it as a query string).
//your routeconfig will be
routes.MapRoute(
name: "default",
url: "{controller}/{file}",
defaults: new { controller = "OrderForm", action = "Index", file = "" }
);
//here no need to have 40 routing table one is enough
//your controller/action will be
public class OrderForm
{
public ActionResult Index(string file)
{
ViewBag.Orderform=file
return View(file);
}
}
//view bag variable is accessible in view as well as in javascript
But I would say as best practice, you can modify default routing to access all urls and navigate it to same controller/action and let that action to return the view. After that use angular / knockout js to handle client side routing and based on it the partial views should be loaded.(still your url will be different for your 40 pages but noo need to pass it as query string)
//your route table will be
routes.MapRoute(
name: "default",
url: "{controller}/{file}",
defaults: new { controller = "OrderForm", action = "Index"}
);
//your controller will be
public class OrderForm
{
public ActionResult Index()
{
return View(file);
}
Navigation should be handled by client side routing
I have this method in my Home controller:
public ActionResult RequestExamReview(string EC)
I have this link in my view (in debug mode):
http://localhost:50909/Home/RequestExamReview/201507LH123
But when I debug into the method and check EC, it is null.
What am I doing wrong? I'm using the default routing, do I need to create a new route definition:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You have a couple options:
You can name the parameter in your method "id" like it is defined in your route
public ActionResult RequestExamReview(string id)
You can specify a new route that has the value ec as a parameter (but I wouldn't recommend this)
If you were to do that it would look something like this:
routes.MapRoute(
name: "Default",
url: "Home/RequestExamReview/{ec}",
defaults: new { controller = "Home", action = "RequestExamReview" }
);
You can use this url instead:
http://localhost:50909/Home/RequestExamReview?EC=201507LH123
You can use the Bind attribute to rename your parameter to id
public ActionResult RequestExamReview([Bind(Prefix="id")] string EC)
I wouldn't recommend adding a new route because it is best to keep the number of routes small to reduce complexity. You will find that as your app grows, managing the number of routes will become painful if you add a new route just to custom name your variables. In your case I would recommend just using the url with the query string values.
I have a SiteController class in my MVC4 project,the 'Site' url is working fine, but I need a dynamic url part right after 'Site', I need the url to look like this:
mysite.com/Site/{DYNAMICSTRING}/Users(or whatever)
{DYNAMICSTRING} can be the name of a subsite, so the controller should check if that subsite does actually exist in the database.
Right now I'm using Query strings, but that is not what my client wants.
How can I do that?
Additional details
My routing:
routes.MapRoute(
"Subdomain", // Route name
"{controller}/{action}/{dynamicString}", // URL with parameters
new { controller = "Site", action = "Subdomain" } // Parameter defaults
);
My controller:
public ActionResult Subdomain(string dynamicString)
{
return View();
}
the value of dynamicString is null when I navigate to: /Site/Subdomain/SomeString
You have to configure routing. For example if you have Homecontroller:
public class HomeController:Controller
{
public ActionResult Subdomain(string dynamicString)
{
return View();
}
}
then you have to configure your routing something like that
routes.MapRoute(
"Subdomain", // Route name
"{controller}/{action}/{dynamicString}/anyOtherParams", // URL with parameters
new { controller = "Home", action = "Subdomain", dynamicString = "" } // Parameter defaults
);
You can do it like this:
routes.MapRoute(
name: "Default", // Route name
url:"Site/{dynamicstring}", // URL with parameters
defaults: new {controller = "Site", action = "Index" } // Defaults
);
you can keep adding parts to the url part like so
url:"Site/{dynamicstring}/{anythingelse}" // url:"Site/{dynamicstring}/{anythingelse}/{bla}/Test/{hello}/etc..."
or you can also have a catch all route like this:"
routes.MapRoute(
name: "Default", // Route name
url:"{*all}", // catch all
defaults: new {controller = "Site", action = "Index" } // Defaults
);
and fetch all other parts in your controllers index action, by splitting them on /
Make sure you put the custom route before your default otherwise the default route will pick it up.
in your controller you get something like this:
public ActionResult Index(string dynamicstring, string anythingelse)
{
return View();
}
and if you then pass in a url like this:
http://www.mysite.com/Site/test.nl/jordy
your dynamicstring will have the value "test.nl" and your anythingelse will have "jordy"
I hope this helps