ASP.NET MVC attribute routing not hitting correct controller - c#

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)
{
}
}

Related

Multiple controller types were found that match the URL in mvc app

I have a weird bug using attribute routing with the following two controllers:
[Route("{action=Index}")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
[RoutePrefix("search")]
[Route("{action=Index}")]
public class SearchController : Controller
{
public ActionResult Index(string searchTerm)
{
return View();
}
}
And in the route config:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
As you can see, the second controller should have a prefix of search
However if I go to dev.local/search?searchterm=test
I get the error
The request has found the following matching controller types:
Marshalls.WebComponents.Web.Controllers.SearchController
Marshalls.WebComponents.Web.Controllers.HomeController
If I remove the [Route("{action=Index}")] from the homecontroller, it will work fine, but then I cannot get to the homepage using http://dev.local/
This hasn't happened before and usually works ok so I'm wondering if anyone can spot anything obvious I have messed up
Add RoutePrefix for HomeController and move Route from controller to methods/actions.
Empty string in Route and RoutePrefix attributes means that this controller or action is default.
http://dev.local/ => HomeController and Index action
http://dev.local/search?searchTerm=123 => SearchController and Index action
Please keep in mind that only one controller can have empty RoutePrefix and only one action in controller can have empty Route
[RoutePrefix("")]
public class HomeController : Controller
{
[Route("")]
public ActionResult Index()
{
return View();
}
}
[RoutePrefix("search")]
public class SearchController : Controller
{
[Route("")]
public ActionResult Index(string searchTerm)
{
return View();
}
}

ASP.NET Core route of a controller and the index action

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.

Asp.net MVC - Area Attribute Routing is not working

I have code like below
[RouteArea("Client")]
public Class LoginController : Controller {
[Route("register")]
public ActionResult SignUp() {
return View();
}
}
Attribute routing unfortunately is not working in the areas :/, if I will remove "register" route for signup, it will work just for for client/signup, but with route "register" it is not working.
I have added [RouteArea()], tried with [RoutePrefix] but nothing is working correctly "Route Area" just enabled to use it with views (before that Razor couldn't find the view).
What am I doing wrong ?
Ok I HAVE FOUND THE SOLUTION.
1 Remove Area registration class from your area
2 Use this convention :
[RouteArea("Client")]
[RoutePrefix("login")]
[Route("{action}")]
public class LoginController : Controller
{
[Route("")]
// GET: Client/Login
public ActionResult Index()
{
return View();
}
[Route("register")]
// GET: client/login/register
public ActionResult SignUp()
{
return View();
}
}
Now you can use any route you want, with any prefix :)

ASP.NET MVC 3 Controller routing

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

Why is this URL like this in MVC3?

I was under the impression that every View in your application has it's own unique URL. For example:
Home/Index
Home/Test
Home/Error
Home/Help
In my Upload controller I call on the Error view. Yet the URL stays on what it was before, not changing to reflect the error url.
[HttpPost]
public ActionResult Index(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
*Snip for brevity, everything is peachy here.*
return View();
}
else
{
return View("Error");
}
}
Any suggestions why this is the case?
Shouldn't the URL be /Upload/Error? Thank you for your help. :)
URLs do not map to Views.
URLs map to Controller actions.
See this http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx
If you want a URL of /Upload/Error
You could make:
public ActionResult Error()
{
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
*Snip for brevity, everything is peachy here.*
return View();
}
else
{
return RedirectToAction("Error","Upload");
}
}
You are returning the content of the View. If you want the URL to change, you need to RedirectToAction()
If you want the URL to change to /Upload/Error, here's what you would add to your UploadController:
public ActionResult Error()
{
return View();
}
Then, instead of returning the Error view, you would call: return RedirectToAction("Error","Upload");.
This basically shows the difference between controllers, actions, and views - controller actions can return any view they want (or other ActionResult) to a request, but only on one URL, unless they "reroute" the request to another action.
In ASP.NET MVC every URL maps to a controller/action. So you can return whatever view from your controller action, this doesn't change the URL.
If you want to redirect to an error Page, then either include a ErrorController in your project or an Error action in your FileUploadController and then do a Redirect to the appropriate action:
public class ErrorController : Controller
{
public ActionResult FileUploadError()
{
return View(); //returns view "FileUploadError"
}
}
public class FileUploadController : Controller // the controller you use to upload your files
{
public ActionResult Error()
{
return View(); //return view "Error"
}
public ActionResult Index(HttpPostedFileBase excelFile) // action from your post
{
//... do the upload stuff
else
{
return RedirectToAction("Error"); // if you want to use the Error action in this controller;
// or
return RedirectToAction("FileUploadError", "Error"); // if you want to use the action on the ErrorController
}
}
}

Categories