I have a web shop. I have some controllers and views to match /Admin/Index, /Admin/ShowProducts etc. all the stuff.
I want to be able to make a "custom" url that is always the same, and it should be in the root of my domain, like this:
www.mydomain.com/shop/myproduct
Where the shop is static and does not change, and the "myproduct" changes according toeach product (it is effectively an ID). How do I do this without getting the controller method displayed within the URL which is the default way?
Is this a new Route? or can I do something on a controller?
The default MVC route almost does it already.
Look at the Controller = and Action =
So make a URL like:
/shop/{id}
Controller = ShoppingController, Action = Shop
Note I added an assumption that the user doesn't have to specify the product, and you get some string to tell you it's the defaultproduct, you can alternatively use UrlParameter.Optional and you will get null for id
Your controller will look like:
public class ShoppingController : Controller
{
public ActionResult Shop(string id)
{
if (string.IsNullOrEmpty(id) || string.Equals(id, "DefaultProduct", StringComparison.OrdinalIgnoreCase))
{
// Do something to please the user
}
// Get product by id
return View();
}
}
And the routing code:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Shopping",
url: "shop/{id}",
defaults: new { controller = "Shopping", action = "Shop" , id = "DefaultProduct" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Related
routes.MapRoute(
name: "MyRoute",
url: "{Product}/{name}-{id}",
defaults: new { controller = "Home", action = "Product", name = UrlParameter.Optional , id = UrlParameter.Optional }
);
my routemap and i want my url in product action be like = http://localhost:13804/Wares/Product/name-id
but now is like =
http://localhost:13804/Wares/Product/4?name=name
When defining a route pattern the token { and } are used to indicate a parameter of the action method. Since you do not have a parameter called Product in your action method, there is no point in having {Product} in the route template.
Since your want url like yourSiteName/Ware/Product/name-id where name and id are dynamic parameter values, you should add the static part (/Ware/Product/) to the route template.
This should work.
routes.MapRoute(
name: "MyRoute",
url: "Ware/Product/{name}-{id}",
defaults: new { controller = "Ware", action = "Product",
name = UrlParameter.Optional, id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Assuming your Product action method accepts these two params
public class WareController : Controller
{
public ActionResult Product(string name, int id)
{
return Content("received name : " + name +",id:"+ id);
}
}
You can generate the urls with the above pattern using the Html.ActionLink helper now
#Html.ActionLink("test", "Product", "Ware", new { id = 55, name = "some" }, null)
I know its late but you can use built-in Attribute Routing in MVC5. Hope it helps someone else. You don't need to use
routes.MapRoute(
name: "MyRoute",
url: "{Product}/{name}-{id}",
defaults: new { controller = "Home", action = "Product", name = UrlParameter.Optional , id = UrlParameter.Optional }
);
Instead you can use the method below.
First enable attribute routing in RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
Then in WaresController
[Route("Wares/Product/{name}/{id}")]
public ActionResult Product(string name,int id)
{
return View();
}
Then to navigate write code like this in View.cshtml file
Navigate
After following above steps your URL will look like
http://localhost:13804/Wares/Product/productname/5
I got a site with only this Route:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Image", action = "Image", id = UrlParameter.Optional }
);
}
This is the controller:
public class ImageController : Controller
{
public ActionResult Image(int? id)
{
if (id == null)
{
// Do something
return View(model);
}
else
{
// Do something else
return View(model);
}
}
}
Now this is the default action so i can access it without an ID just by directly going to my domain. For calling the id it works just fine by going to /Image/Image/ID. However what i want is calling this without Image/Image (so /ID). This doesn't work now.
Is this a limitation of the default Route or is there a way to get this to work?
Thanks
Create a new route specific for this url:
routes.MapRoute(
name: "Image Details",
url: "Image/{id}",
defaults: new { controller = "Image", action = "Image" },
constraints: new { id = #"\d+" });
Make sure you register the above route before this one:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Otherwise it will not work, since the default route will take precedence.
Here I'm stating that if the url contains "/Image/1" then the ImageController/Image action method is executed.
public ActionResult Image(int id) { //..... // }
The constraint means that the {id} parameter must be a number (based on the regular expression \d+), so there's no need for a nullable int, unless you do want a nullable int, in that case remove the constraint.
Here's an example of my set up:
public class UserController : Controller
{
public ActionResult Index(int? id) { ... }
[HttpPost]
public ActionResult DoSomething(int id) { ... }
public ActionResult Search([params]) { ... }
}
and I want to be able to access them via these routes:
/app/User/{id}
/app/User/DoSomething/{id}
/app/User/Search/
I tried setting up my routes like this, but then if I try to navigate to /app/User/Search/ or post to /app/User/DoSomething/, the Index Action is hit instead.
routes.MapRoute(
name: "UserWithoutIndex",
url: "User/{id}",
defaults: new { controller = "User", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
How can I do this? I think it would work to just specify each specific action in it's own route before the UserWithoutIndex route above, but I have multiple actions and I don't want to have to create a route specific to each action in the controller.
The problem is your first route will match any two-segment URL which includes the examples you provided; /app/User/Search/ and /app/User/DoSomething/ and the values Search and DoSomething will be placed in the id place holder respectively. Then because the first route is being matched you are receiving Index for the action. If your id will take on some format specifically you could specify a constraint for it in the first route like so:
routes.MapRoute(
name: "UserWithoutIndex",
url: "User/{id}",
defaults: new { controller = "User", action = "Index", id = UrlParameter.Optional },
constraints: new { id = "your regex here" }
);
If you constraint can be specific enough to the format of the id than things like Search and DoSomething won't match and the route won't match so the next route will be tried.
Also, if there will always be an id specified in the scenarios where you want the first route to be targeted you should remove the id = UrlParameter.Optional default so that way the id will be required and the route will ONLY match two-segment URLs because as it is now with the id being optional the route will also match one-segment URLs.
I have a controller with an index action.
public ActionResult Index(int id = 0)
{
return view();
}
I wish to pass id into the index action, however it doesnt appear to work in the same way as the details action.
e.g. if I want to pass id 4 into the index action, I have to visit url:
http://localhost:8765/ControllerName/?id=4
With the details Action... I can do this.
http://localhost:8765/ControllerName/Details/4
What I want to do with Index is something like...
http://localhost:8765/ControllerName/4
When I visit this url, I get an error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /fix/1
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.17929
Is this possible? How can I get MVC to automatically treat the index action in the same way as the details one?
Thanks
UPDATE - MY CURRENT ROUTES CONFIG
public class RouteConfig
{
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 }
);
}
}
UPDATE NEW RouteConfig Class still doesn't work when I visit localhost:1234/Fix/3
public class RouteConfig
{
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: "FixIndexWithParam",
url: "Fix/{id}",
defaults: new { controller = "Fix", action = "Index", id = UrlParameter.Optional });
}
}
Update It's worth pointing out, /ControllerName/Index/4 should work with the default route.
With the default route there, it expects the second parameter to be the controller name.
So with the Default Route /ControllerName/4 is being interpereted as ControllerNameController Action 4, which of course does not exist.
If you add
routes.MapRoute(
name: "IndexWithParam",
url: "{controller}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
before the default one it would allow
/Home/4 to be routed to HomeController action Index with id=4
I have't tested this, it may conflict with the default. You may need to specify the controller explicitly in the route, ie:
routes.MapRoute(
name: "HomeIndexWithParam",
url: "Home/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
(Obviously, replace Home with whatever controller you're actually wanting to route to)
I have a url
http://www.roadkillwiki.org/Page/Index/documentation
which I want to turn into
http://www.roadkillwiki.org/Page/documentation
That could also be something like http://www.roadkillwiki.org/Page/my-url-with-spaces - the parameter is a string. The route setup I've tried is:
routes.MapRoute(
"ControllerDefault",
"{controller}/{id}",
new { controller = "Page", action = "Index", id = UrlParameter.Optional }
);
However this is interfering with the default "id" route that MVC projects come with. Is there any way of achieving this?
You don't need to lose the default route. The key to avoiding your routes interfere with each other is to order them so the more specific rules precede the less specific ones. For example:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{slug}",
new { controller = "Page", action = "Index" }
);
// Default MVC route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Then your PageController would look like this:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(string slug)
{
// find page by slug
}
}
That said, I would strongly advice you to do this instead:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{id}/{slug}",
new { controller = "Page", action = "Index", slug = UrlParameter.Optional }
);
// MVC's default route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
And your PageController:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(int id)
{
// find page by ID
}
}
By including the page ID either at the beginning of your URL (like StackOverflow does) or at the end, you can then just ignore the slug, and instead retrieve your pages by ID. This will save you a ton of headaches if your users change the page name. I have gone through this and it's painful; you basically have to keep a record of all names your pages have had in the past, just so your visitors/search engines don't get a 404 every time a page is renamed.
Hope this helps.
If you don't need a default route that came with project template you can set up one like this:
routes.MapRoute(
"ControllerDefault",
"{controller}/{pagename}",
new { controller = "Page", action = "Index" }
);
And than in your controller you would have an action:
public ActionResult Index(string pagename)
{
//do something
}