So I am a little confused as to how to handle some MVC Routing
I have an AdminController
public class AdminController : Controller
{
//
// GET: /Admin/
public ActionResult Index()
{
return View();
}
public ActionResult Users()
{
return View();
}
public ActionResult Books()
{
return View();
}
}
Which works fine. So I can go to /Admin/Books
This is the admin menu for managing books. Now in there I'd like to be able to route like
/Admin/Books/ViewBook/10
or
/Admin/Books/Add
Something like that. I can't seem to grasp how to route these things that way.
I made a controller
AdminBookController
public class AdminBooksController : Controller
{
//
// GET: /AdminBooks/
public ActionResult List()
{
return View();
}
public ActionResult Add()
{
return View();
}
[HttpGet]
public ViewResult BookDetails(Guid guid)
{
return View();
}
[HttpPost]
public ViewResult BookDetails(ModifyBook Book)
{
if (ModelState.IsValid)
return View("Book successfully Edited!");
else
return View();
}
}
}
but I don't want it to be /AdminBooks I feel like /Admin/Books/Action/Param is much nicer.
Thanks in Advance!
If you want those urls to map to your AdminBooks controller, you'll need to map the following routes (in this order):
// maps /Admin/Books/ViewBook/{id} to AdminBooksController.BookDetails(id)
routes.MapRoute(
"AdminBooks_ViewBook", // Route name
"Admin/Books/ViewBook/{id}", // URL with parameters
new { controller = "AdminBooks", action = "BookDetails", id = UrlParameter.Optional } // Parameter defaults
);
// maps /Admin/Books/{action}/{id} to AdminBooksController.{Action}(id)
routes.MapRoute(
"AdminBooks_Default", // Route name
"Admin/Books/{action}/{id}", // URL with parameters
new { controller = "AdminBooks", action = "List", id = UrlParameter.Optional } // Parameter defaults
);
Note: be sure to put these mappings before the default MVC route.
Consider creating an Admin Area and adding a BookController to that Area. See the following link for a walkthrough:
http://msdn.microsoft.com/en-us/library/ee671793.aspx
You can add a new route in your Global.asax file.
See this question:
Use MVC routing to alias a controller
Related
I have a couple of controllers like this:
[RoutePrefix("side-navigation")]
public class SideNavigationController : BaseController
{
[Route("{pathname}")]
public ActionResult Index(string pathname)
{
SideNavigationPopoutModel model = _sideNavFactory.Value.CreatePopout(pathname);
if (model != null)
{
return View(model);
}
return HttpNotFound();
}
}
public class CatchAllController : BaseController
{
public ActionResult Index(string pathname)
{
CatchAllModel model = _catchAllModelFactory.Value.Create(pathname);
if (model != null)
{
// TODO: Do we need this - what does it do?
// TempData.Restore(this);
return View(model);
}
return HttpNotFound();
}
}
But I cannot seem to get to my index action in the side navigation controller - if I browse to localhost/side-navigation/test it's hitting the catch all controller with side-navigation/test as it's pathname instead of the side navigation one with test as the pathname.
Can anyone see anything I am doing wrong here or how to make the side navigation controller work?
This is the route config:
// MVC attribute routing
routes.MapMvcAttributeRoutes();
// Default catch all route
routes.MapRoute(
"Default",
"{*pathname}",
new { controller = "CatchAll", action = "Index" });
Weirdly, if I change the route of the side navigation index to test/{pathname} and browse to side-navigation/test/test it will work and the controller will be hit but I don't want to add anything before the pathname
It seems that you are not using [Area] also put attribute [Route("[action]")] above method.
Ok I have fixed this by adding an asterisk before the pathname:
[RoutePrefix("side-navigation")]
public class SideNavigationController : BaseController
{
[Route("{*pathname}")]
public ActionResult Index(string pathname)
{
}
}
If anyone can explain why this works and without an asterisk doesn't, it would be greatly appreciated, as I also have a product controller set up in exactly the same way that doesn't need the asterisk
[RoutePrefix("side-navigation")]
public class SideNavigationController : BaseController
{
[Route("{*pathname}")]
public ActionResult Index(string pathname)
{
}
}
I'm trying to set a route of my controller while also be able to navigate the index without typing Index, here's what I tried:
My route configuration
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Try #1
// My controller
[Route("panel/admin")]
public class MyController...
// My index action
public IActionResult Index()...
Problem: This doesn't work, all the actions become accessible at panel/admin so I get an error saying Multiple actions matched.
Even when setting the route of my index action to Route(""), doesn't change anything.
Try #2
// My controller
[Route("panel/admin/[action]")]
public class MyController...
// My index action
[Route("")]
public IActionResult Index()...
Here, the index route doesn't change, it stays panel/admin/Index.
What I want
I want to be able to access my index action when navigating to panel/admin and I also want my other actions to work with just their method names like panel/admin/UsersList.
Complete controller
[Route("panel/admin/[action]")]
public class MyController
{
[Route("")]
public IActionResult Index()
{
return View();
}
public IActionResult UsersList()
{
var users = _db.Users.ToList();
return View(users);
}
// Other actions like UsersList
}
Thank you.
Reference Routing to controller actions in ASP.NET Core
With attribute routes you have to be very specific about desired routes to avoid route conflicts. Which also means that you will have to specify all the routes. Unlike convention-based routing.
Option #1
[Route("panel/admin")]
public class MyController {
[HttpGet]
[Route("")] //GET panel/admin
[Route("[action]")] //GET panel/admin/index
public IActionResult Index() {
return View();
}
[HttpGet]
[Route("[action]")] //GET panel/admin/UsersList
public IActionResult UsersList() {
var users = _db.Users.ToList();
return View(users);
}
// Other actions like UsersList
}
Option #2
[Route("panel/admin/[action]")]
public class MyController {
[HttpGet] //GET panel/admin/index
[Route("~/panel/admin")] //GET panel/admin
public IActionResult Index() {
return View();
}
[HttpGet] //GET panel/admin/UsersList
public IActionResult UsersList() {
var users = _db.Users.ToList();
return View(users);
}
// Other actions like UsersList
}
The tilde (~) in [Route("~/panel/admin")] overrides the route prefix on the controller.
Tip
While using multiple routes on actions can seem powerful, it's better
to keep your application's URL space simple and well-defined. Use
multiple routes on actions only where needed, for example to support
existing clients.
I currently have a Products controller with a hardcoded 'Product' actionresult per product (as the products are fixed and do not change):
site.com/Products/Product
site.com/nl/Products/Product
This results in a single page per product containing all information.
Now I would like to create multiple pages per product to highlight some features or options instead of showing a single product page.
e.g.:
site.com/nl/Products/Product/Detail1
site.com/nl/Products/Product/Option2
site.com/nl/Products/Product/Option16
What is the best way to do this?
Should I create e.g. ProductDetail1 action and a ProductOption2 action?
You can responce different views in a single action
public class ProductsController : Controller
{
public ActionResult Product(int id, string view,)
{
Product prod = Context.GetProduct(id);
if(!string.IsNullOrEmpty(view))
{
switch(view.ToLower()){
case "detail": return View("Detail", prod.Detail);
case "option1": return View("Option1", prod.GetOption(1));
case "option2": return View("Option2", prod.GetOption(2));
}
}
return View();
}
}
Well you have your Controller Method:
public class ProductsController : Controller
{
public ActionResult Product(string option)
{
//here your logic
return View();
}
}
You can change your Default Route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{option}",
defaults: new { controller = "Home", action = "Index", option = UrlParameter.Optional }
);
Now if you call url like site.com/nl/Products/Product/Detail1 option object in your controller will have value Detail1. And you can do anything what whould you like with this param.
I need my page names to have a dash in the name. E.G our-vision
I'm new to MVC & c# so I may be going about all this wrong.
Here is my controller:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
//
// GET: /our-vision/
public ActionResult ourVision()
{
return View();
}
}
And then in my views, I have Views/Home/ourVision.cshtml.
When I compile and go to http://localhost/ourVision it works, but when I go to http://localhost/our-vision it does not.
Here is my routing:
routes.MapRoute(
"Default", // Route name
"{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
You'll need to do a few things in order to achieve that.
First, to achieve our-Vision, you'll need to give your action method the ActionName attribute, like so:
[ActionName("our-Vision")]
public ActionResult ourVision()
Next, you'll have to rename your ourVision.cshtml view to be our-Vision.cshtml
Finally, whenever you're using Url.Action or ActionLink, you need to use our-Vision and not vision, like so:
Url.Action("our-Vision", "Home");
IMHO
The best way to do this - is define new route in route engine:
routes.MapRoute(
"OurVision", // Route name
"our-vision", // URL with parameters
new { controller = "Home", action = "ourVision" } // Parameter defaults
);
I am teaching myself asp .net mvc3.
I want to create a user account page which has 3 tabs in it - say YourAddress, YourPhotos and YourProfile.
The 3rd tab (YourProfile) has 2 more subtabs in it ... ChangeDetails and DeactiaveAccount.
They are all dynamic pages and therefore I want to keep them as separate pages.
Basically the urls would be:
localhost/MyHome/YourAddress
localhost/MyHome/YourPhotos
localhost/MyHome/YourProfile/ChangePassword
localhost/MyHome/YourProfile/DeactivateAccount
(As requested I have changed the generic tab1, tab2 etc to something in real-world scenario)
I am planning to do something like this:
public class MyHomeController : Controller
{
//
// GET: /MyHome/Tab1
public ActionResult Tab1()
{
return View();
}
//
// GET: /MyHome/Tab2
public ActionResult Tab2()
{
return View();
}
//
// GET: /MyHome/Tab3
public ActionResult Tab3()
{
return View();
}
}
How do I handle the subtabs of YourProfile? How do I call a controller within a controller?
What is the best way to accomplish this.
Thanks
Have separate action method for each tab item in your controller.
public class MyHomeController : Controller
{
public ActionResult YourAddress()
{
return View();
}
public ActionResult YourPhotos()
{
return View();
}
public ActionResult YouProfile()
{
return VieW();
}
public ActionResult ChangePassword()
{
return View();
}
public ActionResult DeActivate()
{
return View();
}
}
For the sub tab content, define that route in the global.asax
routes.MapRoute("ChangePass","YourProfile/ChangePassword",
new { controller="MyHome", action="ChangePassword" });
routes.MapRoute("DeActivate","YourProfile/DeActivate",
new { controller="MyHome", action="DeActivate" });
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Always use Url.Action Html Helper method to render a path to an action method.
<div id="tabs">
<ul>
<li>Address</li>
<li>Photos</li>
</ul>
</div>