public class PostController : YourDefinitionController
{
public ActionResult Test(int id ,int title)
{
return View();
}
}
#Html.ActionLink("Postss", "Test","Post" ,new { title = "asdf",id=3 },null)//in Razor view
// here is route registration
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Post",
"{Post}/{Test}/{title}/{id}",
new { controller = "Post", action = "Test",id=UrlParameter.Optional, title=UrlParameter.Optional }
);
routes.MapRoute(
"Defaultx", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
I expected to see link like /Post/Test/asdf/3 but it is /Post/Test/3?title=3
Why ? How can i fix it?
I would suggest cleaning your code a bit, because there are many things done based on conventions. So keeping code consistent often helps.
public ActionResult Test(string title ,int id) // Order is switched and title changed to string
Edit: The problem is with wrong route path. You have to change it to "Post/Test/{title}/{id}"
// changed route path expression
routes.MapRoute(
"Post",
"Post/Test/{title}/{id}",
new { controller = "Post", action = "Test", title = UrlParameter.Optional, id = UrlParameter.Optional }
);
Btw: If you are going to play with routes a bit more, Phil Haack's blog will be a great resource.
Related
I have 2 controllers:
public class HomeController : Controller
{
public ActionResult Index(string id) //id is category slug
{
if(string.IsNullOrEmpty(id))
id = "MainCategory";
var model = getCategoryPageModel(id);
return View(model);
}
}
public class PostController : Controller
{
public ActionResult Index(string id) //id is post slug
{
var model = getPostModel(id);
return View(model);
}
}
And this is my route config:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//angular route for admin section
routes.MapRoute(
name: "AngularCatchAllRoute",
url: "Admin/{*any}",
defaults: new { controller = "Admin", action = "Index"}
);
//route which includes language
routes.MapRoute(
name: "DefaultLocalized",
url: "{lang}/{controller}/{action}/{id}",
constraints: new { lang = #"(\w{2})|(\w{2}-\w{2})" }, // en or en-US
defaults: new { controller = "Home", action = "Index", id = "" }
);
//do I need this one??
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I want to have two types of custom routes:
www.mysite.xyz/categoryName - calls Home/Index/id with default language ('sr')
www.mysite.xyz/en/categoryName - calls Home/Index/id with language 'en'
www.mysite.xyz/Post/postID - calls Post/Index/id with default language('sr')
www.mysite.xyz/en/Post/postID - calls Post/Index/id with language 'en'
My 'DefaultLocalized' route already works fine with default and custom language route part, but my url has to contain all route parts: controller/action/id. I just want to simplyfy urls to be more readable to users.
Actually I made it work for post with 'lang' to be mandatory:
www.mysite.xyz/sr/Post/postID - but I want 'sr' to be default like in 'DefaultLocalized' route, there I don't have to set lang to be 'sr'...
I already have tried some answers from other similar questions but I did not make it work.
You can use attribute routing as below:
[Route("~/{cate}")]
[Route("{lang}/{cate}")]
public IActionResult Index(string lang, string cate)
{
return View();
}
That work for me with urls:
http://[host]/Mobile
http://[host]/en/Mobile
Hope this help!
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
This is my code:
routes.MapRouteLowercase(
name: "productadd",
url: "product/add",
defaults: new
{
controller = "Product",
action = "Add"
}
, namespaces: new[] { "project.Controllers" });
routes.MapRouteLowercase(
name: "productlike",
url: "product/like",
defaults: new
{
controller = "Product",
action = "Like"
}
, namespaces: new[] { "project.Controllers" });
routes.MapRouteLowercase(
name: "productshow",
url: "product/{id}/{seoName}",
defaults: new
{
controller = "Product",
action = "Get",
id = UrlParameter.Optional,
seoName = UrlParameter.Optional
}
, namespaces: new[] { "project.Controllers" });
I want a solution for writing less codes, actually a template for productshow and another template for product actions
you can use Attribute Based Routing in MVC. This is available by default in MVC5, or can be installed as a NuGet package in MVC4.
With Attribute Based Routing, you can define Attributes on your action methods, rather than magic string matches in the routing table. You can also perform more advanced type checking, such as minimum and maximum values, and optionally name routes for easy reference in your Razor.
as an example:
[RoutePrefix("product")]
public class ProductController : Controller {
//route /product
[Route]
public ActionResult Index() { ... }
//route /product/add
[Route("add")]
public ActionResult Add() { ... }
//route /product/like
// Like
[Route("like", Name="productlike")]
public ActionResult Like() { ... }
//route /product/{id}/{seoName}
[Route("{id?}/{seoName?}")]
public ActionResult Get(int? id, string seoName) { ... }
}
Saman, you can create a default route, like this one below. I'm not sure if that will work with "MapRouteLowercase", you can give it a try.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional }
);
This link has more options if you want.
I have a Web API 2 API Controller
[RoutePrefix("things")]
public class ThingsController : APIController
{
[Route("{thingId}", Name="Things.Get")]
public Thing Get(string thingId)
{
//snip...
}
}
And all is well... except I'd like to pass the URL for that route into some JS so that it can construct URLs to request Things
So in the razor view I've tried
myJavascriptObject.thingUrl = '#Url.Action("Get", "Things", new {area=""})';
but I get a blank string
I've tried giving the controller route an explicit name and using
#Url.RouteUrl("Things.Get", new {areas="", thingId="foo"}") b
ut that's an empty string too.
Am I being silly? Should this work?
Update after more trying of things.
On an MVC controller (Where I need to know the API Controller route)
var something = Url.Action("Get", "Things", new {area = ""});
var somethingWithId = Url.Action("Get", "Things", new { area = "", siteId = "fakeId" });
var somethingElse = Url.RouteUrl(new {Action = "Get", Controller = "Things", Area = ""});
var somethingElseWithId = Url.RouteUrl(new { Action = "Get", Controller = "Things", Area = "", siteId = "fakeId" });
These are all null.
If I remove the area="" condition then I get a route but with the Area name in it and the route doesn't have the area name in it.
?!
TL;DR - if something is weird spike it in a fresh environment
Leaving an answer here in the very remote case it helps someone else.
As somebody pointed out to me that this worked for them I created a new MVC & API project and looked at the default routing.
Ours looked like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "home",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new [] { "Another.Project.Controllers" }
);
}
WAT?
I replaced it with the default route config:
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 }
);
}
Everything else still works and I can get the route I want.
We definitely aren't using the other namespace in this project and I'm guessing that namespaces was introduced either when this solution was part of another one (which it did use to be) or by the refactoring that moved it.
And so I've reached the natural conclusion of any really annoying bug: "How did this ever work?!"
Here is my route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{testId}/{lg}",
defaults: new { controller = "Home", action = "Index", testId = UrlParameter.Optional, lg = UrlParameter.Optional }
);
Here is my controller code
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index(int testId,string lg)
{
return View();
}
public ActionResult Index2(int testId, string lg)
{
return View();
}
}
Here is my view code (Index.cshtml)
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Url.Action("Index")<br/>
#Url.Action("Index2")
When I open "/Test/INdex/1/EN"
This gives me
/Test/Index/1/EN
/Test/Index2
The first link is ok because it uses the current route value (lg =EN and testId = 1)
But the second is not using the values, I don't get it !
I wouldn't have expected the first link to have route values, honestly. Maybe the view engine is smart enough to know that it's linking to the current route and automatically populates them?
In any event, linking to something entirely different isn't going to populate route values by default. The framework has no reason to believe that those route values are applicable to an entirely different action.
Specify them explicitly:
#Url.Action("Index2", new { testId = 1, lg = "EN" })
I found a workaround : the route parameters must be before the action ! :
routes.MapRoute(
name: "Default",
url: "{controller}/{testId}/{lg}/{action}",
defaults: new { controller = "Home", action = "Index", testId = UrlParameter.Optional, lg = UrlParameter.Optional }
);
If someone can say me why...