I have been working for ~1 year with MVC now but haven't really ever had to look deep into routing and just used the normal Defaults. In a new Application I now need to adjust these and cant figure out how it exactly works.
My Situation
I have an application starting with a View with a List of different Calendars in a DB.
Each of these Calendars should have a url to them like the following:
home/calendarName/weekNumber
--> controller/dynamic string based on name of calendar/dynamic int based on selected week
The issue is that I don't want to create a action for every single CalendarName--> calendar.
Is my Problem understandable?
This is my Startup.cs Code atm:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "test",
template: "test",
defaults: new { controller = "Home", action = "Name-Of-Calender", id="WeekID_Of_Calendar" });
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=KalenderListe}/{id?}");
});
This is the URL-Action in the View atm.
"#Url.Action("View", "Home", new {KalenderName= kalender.KalenderName,Id= kalender.KalenderId})"
If it isn't clear what im trying to do, let me know and I'll adjust it. Thanks in advance.
For your problem you can use Attribute routing on action method. Just write the string in your route just like below
`
[Route("Home/About")]
public IActionResult MyAbout()
{
return View("About");
}
[Route("Home/Contact")]
public IActionResult MyContact()
{
return View("Contact");
}
`
You can also change as you want like
`
[Route("About/Home")]
public IActionResult MyAbout()
{
return View("About");
}
`
You can use attribute routing on your action within your home controller.
[HttpGet("{calendarName}/{id}")]
public void ActionResult GetCalendar(string calendarName, int id)
{
...
}
Or if you want to pass the as a optional query
[HttpGet("{calendarName}")]
public void ActionResult GetCalendar(string calendarName, [FromQuery]int id)
{
...
}
Related
I have sample .net core mvc application, im testing SEO friendly url creating ,
which is posted Here
I have created all the code as in the post.
My testing controller action is as follows
public IActionResult Index(int id, string titl)
{
//string titl = "";
var viewModel = new FriendlyUrlViewModel() { Id = 1, Name = "Detail home view." };
string friendlyTitle = FriendlyUrlHelper.GetFriendlyTitle(viewModel.Name);
// Compare the title with the friendly title.
if (!string.Equals(friendlyTitle, titl, StringComparison.Ordinal))
{
return RedirectToRoutePermanent("post_detail", new { id = viewModel.Id, title = friendlyTitle });
}
return View(viewModel);
}
and i have create custom route in startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "post_detail",
template: "FriendlyUrl/index/{id}/{title}"
).MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
when i navigate to
https://xxxxx:2222/FriendlyUrl/index/2/dfdsfdsfds
browser display nothing,
I think issue is with custom routing, but i cant find it,
can someone help on this?
Thanks.
You don't have to add:
routes.MapRoute(
name: "post_detail",
template: "FriendlyUrl/index/{id}/{title}")
That is for the global 'controller/action' map rule.
Instead, to map specified path to action, consider adding an attribute like this:
[Route("FriendlyUrl/index/{id}/{title}")]
public IActionResult Index([FromRoute]int id, [FromRoute]string title)
{
}
FAQ
How to use route name to redirect?
Simply add a name to your route. Like this:
[Route("FriendlyUrl/index/{id}/{title}", Name = "post_detail")]
public IActionResult Index([FromRoute]int id, [FromRoute]string title)
{
}
To route to this action from another controller or action, call it with:
// in action
return RedirectToRoute("post_detail");
// or
return RedirectToRoutePermanent("post_detail");
// I guess you don't have to lose the `Id`:
return RedirectToRoutePermanent("post_detail", new { id = 5 });
How to allow receiving null to title
Simply add a ? to the route template. Like this:
[Route("FriendlyUrl/index/{id}/{title?}", Name = "post_detail")]
public IActionResult Index([FromRoute]int id, [FromRoute]string title)
{
}
I am trying to localize my app's URLs. Unfortunately, most of the pages show me examples of app localization Like :
http://localhost/en-US/Home/Index
This is not what I want. I would to localize the URLs like that:
http://localhost/Welcome
http://localhost/Bienvenue [ welcome word in French ]
The culture has aleady been managed on my side with a cookie and working well with "CookieRequestCultureProvider" class.
So I have this information and localization in pages are OK.
I succeeded to register all the routes I need. Both of example above working and display the page. Thanks to this :
public void Apply(ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
foreach (var action in controller.Actions)
{
var localizedRouteAttributes = action.Attributes.OfType<LocalizedRouteAttribute>().ToArray();
if (localizedRouteAttributes.Any())
{
foreach (var localizedRouteAttribute in localizedRouteAttributes)
{
var localizedVersions = GetLocalizedVersionsForARoute(localizedRouteAttribute.Name); // GetLocalizedVersionsForARoute contains all routes translated and group by culture.
foreach (var localizedVersion in localizedVersions)
{
if (!action.Selectors.Any(s => s.AttributeRouteModel.Template == localizedVersion.Template))
action.Selectors.Add(new SelectorModel(action.Selectors.First()) { AttributeRouteModel = localizedVersion });
}
}
}
}
}
}
So mvc take the last route register in Selectors (if FR, it take FR route).
I can't manage the other routes by this piece of code because it's load with the app. And can't work with a dynamic use (The app permit to change the lang when I want).
Thanks in advance.
I found this example project works: https://github.com/tomasjurasek/AspNetCore.Mvc.Routing.Localization
Once it's set up, you can tag routes with
[LocalizedRoute("culture", "RouteName")]
Do that for each culture you want a unique name for, and the dynamic route it creates will translate to the proper action and execute it. It's also got a tag helper for creating translated links, though if you want to use Url.Action or Html.ActionLink, I find you have to create extension methods that take the culture into account to get them to work fully.
In your case wanting them at the route level instead of /culture/Controller/Action may take some more work, but it might be a useful starting place for you.
look in this little example I hope to help you :)
1) in your controller :
[RoutePrefix("/")]
public HomeController : Controller {
[HttpGet]
[Route("Welcome")]
public ActionResult Index() {
return View();
}
}
And enable it in route table " routes.MapMvcAttributeRoutes(); " like this
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//enable attribute routing
routes.MapMvcAttributeRoutes();
//convention-based routes
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = "" }
);
}
}
I suggest reading this article from this URL:
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
if you have any other question you can ask me
I added these routes to RouteConfig.cs
routes.MapRoute(
name: "NewPositiveRelease",
url: "dispatch/{poNumber}/{article}",
defaults: new { controller = "PositiveReleaseItem", action = "Create"});
routes.MapRoute(
name: "Dispatch",
url: "dispatch",
defaults: new { Controller = "Dispatch", action = "Index"});
In the hopes that I could go to this url
locahost:3000/dispatch/4529707272/171112
To execute the Create action in PositiveReleaseItemController. However, when I navigate to that url, I am seeing the error:
A public action method '4529707272' was not found on controller 'MVCQCPage.Controllers.DispatchController'.
Can someone please help me understand why this doesn't work?
Here is the controller:
using SharedLibrary.Models;
using System.Web.Mvc;
namespace MVCQCPage.Controllers
{
public class PositiveReleaseItemController : Controller
{
// GET: PositiveReleaseItem
public ActionResult Index()
{
return View();
}
public ActionResult Create(string poNumber, string article)
{
return View();
}
public ActionResult Insert(PositiveReleaseItem item)
{
return View();
}
}
}
I tried changing the order of the given routes, but with the same outcome. Please let me know if I can add any details which might help.
Thanks
Shouldn't it be:
"PositiveReleaseItem/{poNumber}/{article}",
Instead of:
"dispatch/{poNumber}/{article}",
I was able to resolve this by simply placing my new route mappings at the top of Routeconfig.cs (above all the others).
Not sure why this worked, as none of my other route maps refer to the Dispatch controller, so its weird that it was complaining about that.
So I know google can penalize a site if you have the same content on multiple urls... unfortunately, in MVC this is too common i can have example.com/, example.com/Home/ and example.com/Home/Index and all three urls would take me to the same page... so how do I make sure that whenever Index is in the url, that it redirects to the same without the Index and of course the same thing with the Home
Perhaps this little library may be useful for you.
This library is not very convinient in your case, but it should work.
var route = routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.Redirect(r => r.MapRoute("home_index", "/home/index")).To(route);
routes.Redirect(r => r.MapRoute("home", "/home")).To(route);
The way I handle this is for default pages like Index is to simply create an explicit route for only one of them. I.e. "example.com/People" would be the route for People/Index, and there would be no valid page at the url "/example.com/People/Index".
The Home example is unique in that it has potentially three different URLs. Again in this case I'd simply create a route for "example.com" for that Index action, and not support the other two urls. In other words, you would never link to the other forms of the URL, so their absence should never cause a problem.
We use a Nuget package called AttributeRouting to support this. When you specifiy a GET route for a page, it overrides the defaults for MVC.
Using AttributeRouting usually you'd map the index to [GET("")] but for the special case of Home where you also want to also support the root URL that omits the controller name , I think you'd also add an additional attribute with IsAbsoluteUrl:
public class HomeController : BaseController
{
[GET("")]
[GET("", IsAbsoluteUrl = true)]
public ActionResult Index()
{...
So I found a way to do it without any external Library...
In my RouteConfig I had to add these two routes at the top, just below the IgnoreRoute
routes.MapRoute(
"Root",
"Home/",
new { controller = "Redirect", action = "Home" }
);
routes.MapRoute(
"Index",
"{action}/Index",
new { controller = "Redirect", action = "Home" }
);
Then I had to create a new Controller called Redirect and I created a method for each of my other Controllers like this:
public class RedirectController : Controller
{
public ActionResult Home()
{
return RedirectPermanent("~/");
}
public ActionResult News()
{
return RedirectPermanent("~/News/");
}
public ActionResult ContactUs()
{
return RedirectPermanent("~/ContactUs/");
}
// A method for each of my Controllers
}
That's it, now my site looks legit. No more Home, no more Index in my URLs, this of course has the limitation of not being able to accept parameters to any of the Index methods of your Controllers though if it was really necessary, you should be able to tweak this to achieve what you want.
Just an FYI, if you want to pass an argument to your Index Action, then you can add a third route like this:
routes.MapRoute(
name: "ContactUs",
url: "ContactUs/{id}/{action}",
defaults: new { controller = "ContactUs", action = "Index", id = UrlParameter.Optional }
);
This will create a URL like this: /ContactUs/14
I am creating a blog engine, and I need a custom route, like this:
localhost/blogname/posts/1
Where blogname should be handled by a BlogsController, and posts will be an action.
How would I define such a route?
I don't think you need to define {controller} in your Url if you define it as a constraint. I think this should work:
routes.MapRoute("Default",
"{action}/{id}",
new { controller = "Blogs", action = "Posts" },
new { controller = "Blogs"});
It might cause problems with other routes though, I'm not sure. If it doesn't work, David's answer of http://site.com/blogs/posts/id is the best way to go.
This feels like a weird approach. If you use the default routing in ASP.NET MVC, you would need one controller class per blog--not something you can easily create on the fly.
If you use the classname BlogsController, then the default routing would work for URLs of the form:
/Blogs/SomeAction/123
Maybe this is what you're looking for:
public class BlogsController : Controller
{
public ActionResult List()
{
return View(GetPostsOrSomething());
}
public ActionResult Posts(int id)
{
return View(new BlogViewModel(id));
}
[HttpPost]
public ActionResult Comment(int id, string comment)
{
// do comment
}
}
And your routing would need to look like this:
routes.MapRoute(
"Blogs", // Route name
"{blog}/{action}/{id}", // URL with parameters
new { controller = "Blogs", action = "List", id = UrlParameter.Optional }
);
Note
Bear in mind, this would match default style URLs, and everything might be routed to your BlogsController. Maybe you could consider a regular expression for the {blog} part of the pattern:
routes.MapRoute(
"Blogs", // Route name
"{blog}/{action}/{id}", // URL with parameters
new { controller = "Blogs", action = "List", id = UrlParameter.Optional },
new { blog = "(blogname1|blogname2|blogname3|etc)" }
);
But, this isn't very flexible either. Any time you added a blog to your site, this regular expression would require an update. I would probably reconsider your URL structure--something similar to the default style.
Something like this
routes.MapRoute(
"Blogs", // Route name
"{blogname}/{action}/{id}", // URL with parameters
new { controller = "Blog", action = "Posts", id = UrlParameter.Optional } // Parameter defaults
);
would work, meaning you could have your blog controller like this:
public class BlogController : Controller
{
public ActionResult Posts(string blogname, int id)
{
... get posts based on blog name and id and return view...
}
}
but then if you want a url like localhost/admin/dostuff/1 to go to an admin controller, how will MVC know that you don't just mean a 'blogname' called 'admin'?
You would need to do something like the regex matching that David suggests, or else specifically add a route for any other controllers you have before you add the Blog route
e.g.
routes.MapRoute(
"Admin Controller Routes", // Route name
"admin/{action}/{id}", // URL with parameters
new { controller = "Admin", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Account Controller Routes", // Route name
"account/{action}/{id}", // URL with parameters
new { controller = "Account", id = UrlParameter.Optional } // Parameter defaults
);
... etc - one for each controller ...
I found this approach to be best for my needs. Very simple, goal is to have the title of the blog as the parameter but also the URL (I didn't want a ? to set a parameter value, I have been told it is not helping SEO. Goal is MYURL.com/blog/my-blog-title
ASP.NET MVC
add to your RouteConfig.cs file
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Blog",
url: "blog/{title}",
defaults: new { controller = "Blog", action = "GetBlog" },
constraints: new { title = #"[\w\-]*" }
);
Add a Controller and call it Blog and then add the following Action to that Controller
[Route("blog/{title}")]
public ActionResult GetBlog(string title)
{
// do what ever code you need to do here to get the blog from the title and pass a model to the view using return View(MyBlogObject)
return View();
}