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
Related
Controller
[HttpGet]
[Route("find-a-doctor")]
public ActionResult FindADoctor()
{
ViewData["sList"] = specialities ;
return View();
}
Route.Config
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = "UserPanelFinADoctor", action = "FindADoctor" },
namespaces: new string[] { "xyz.Controllers" }
);
}
This is the result when I debug code. It says "The resource cannot be found"
I resarched about it , and found this on microsot docs.
Actions are either conventionally routed or attribute routed. Placing
a route on the controller or the action makes it attribute routed.
Actions that define attribute routes cannot be reached through the
conventional routes and vice-versa. Any route attribute on the
controller makes all actions in the controller attribute routed.
So in your example .Since you have registered the route to your action under attribute based routing. Convention routing wont work.
Either of the one should be used, not both.
Link to article - https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-5.0#mixed-routing-attribute-routing-vs-conventional-routing
I know how to create a URL friendly route and i also know how to remove index. But I'm wondering how do I combine the two together?
Using this tutorial https://www.jerriepelser.com/blog/generate-seo-friendly-urls-aspnet-mvc/ I was able to add the following code to allow for url friendly routes.
routes.Add("ProductDetails", new SeoFriendlyRoute("drink/{id}",
new RouteValueDictionary(new { controller = "Drink", action = "Index" }),
new MvcRouteHandler()));
So instead of my url being test.com/index/drink/1 it now becomes test.com/index/drink/coke
The next set of code I have is to remove the index from the url.
routes.MapRoute("DrinkRoute",
"drink/{id}",
new { controller = "Drink", action = "Index" });
This will succesfully convert test.com/index/drink/1 to test.com/drink/1
May I ask how do I combine the two together so that I can have a route that will lead me to the correct controller action and display test.com/drink/coke
RouteConfig
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
You can also achieve the same using attribute routing which would provide more control of the desired routes.
Reference Attribute Routing in ASP.NET MVC 5
First you would need to enable attribute routing by calling routes.MapMvcAttributeRoutes(); in your RouteConfig. Make sure it is registered before convention-based routes.
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Attribute routes
routes.MapMvcAttributeRoutes();
//Default convention-based routes
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
With attribute routing enabled you can specify your routes by annotating your actions and controllers.
[RoutePrefix("drink")]
public class DrinkController : Controller {
[HttpGet]
[Route("{name}")] // GET drink/coke
public ActionResult Index(string name) {
//...use name to get model
return View();
}
//..
}
The above DrinkController.Index action is now mapped to GET drink/coke assuming test.com is the host of the controller as shown in your example.
Any controllers or actions not annotated by routing attributes will default back to the convention based routes (if any) registered in the routing table.
This means that you can have a mix of convention-based and attribute-based routes defined for your controllers.
Note however that once you use the attribute routes on a controller that you will have to use it on all its public actions.
If I understand you correctly, you can achieve desired behavior with RouteConfig.cs:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "DrinkRoute",
url: "drink/{id}",
defaults: new { controller = "Drink", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
In that case the url test.com/drink/coke will be hit with the controller DrinkController.cs and the action method Index. Id will be coke. Source code of this controller:
public class DrinkController : Controller
{
public ActionResult Index(string id)
{
return View();
}
}
You can remove the SEO Routes and give your action or controller full control:
public class DrinkssController : Controller
{
[Route("drink/{drinkName}")]
public ActionResult Index(string drinkName)
{
var model = _drinks.First(x => x.name == drinkName);
return View(model);
}
}
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.
Reddit links are normally like this:
https://www.reddit.com/r/<subreddit>/<topic>
meaning that, the subreddit can be anything depending on how the user created it.
Usually on ASP MVC, We can do it like this:
local/controller/action?subreddit=subname&topic=topicname
but what if I want it to be something like this:
local/controller/action/subname/topicname ?
The keyword for this feature is attribute routing in ASP.NET MVC. There is a lot of information availbale in blogs etc.
With the Route-Annotation you can decorate your action and define a mapping between URL parts and parameters for the action call.
public class ExampleController : Controller
{
[Route("r/{subreddit}/{topic}")]
public ActionResult Topic(string subreddit, string topic)
{
//Logic goes here
}
}
Furthermore the attribute routing has to be activted in the RouteConfig.cs with routes.MapMvcAttributeRoutes(); like
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Then you can call the Topic-Action of the ExampleController by http://localhost:PORT/r/reddit/topic.
I would like to suppress the controller from the route, and it worked fine using this:
routes.MapRoute(
name: "HomePages",
url: "{action}",
defaults: new { controller = "Home", action = "Index" }
);
The problem is when doing the same for a different controller like "Account", the first option only will take effect :
routes.MapRoute(
name: "LoginRoute",
url: "{action}",
defaults: new { controller = "Account", action = "Login" }
);
My objective is to hide the controller from the route, so i can directly access mysite.com/login and mysite.com/index, how to achieve that when login is under Account controller and index is under Home controller?
How to specify the second option for the Account actions, and keep the first for the Home actions?
Have you thought about using the Routing attributes e.g:
[Route("{getId:int}")]
public ActionResult Show(int getId) { ... }
you can use this in conjunction with the old way of routing. You do need to explicitly set this functionality in your config:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
}
I have found the routing Attributes makes it very easy to set good routes on my controller methods. This also bleeds into web api and RESTFul stuff as well.