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
Related
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)
{
...
}
Hi I have problem with routes in plugin, in nopcommerce 3.6
I have in folder Controller TestPohodaController.cs contains method ImportProductInfo()
There is my RegisterRoutes:
namespace Nop.Plugin.Test.Pohoda
{
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Plugin.Test.Pohoda.ImportProductInfo",
"Plugins/TestPohoda/ImportProductInfo",
new { controller = "TestPohoda", action = "ImportProductInfo" },
new[] { "Nop.Plugin.Test.Pohoda.Controllers" }
);
}
public int Priority
{
get
{
return 0;
}
}
}
}
Installation to nopCommerce is ok, but when I go to mypage/Plugins/TestPohoda/ImportProductInfo page return 404.
I need url of TestPohodaController to call this controller from economic system. Can You help me please? Thanks.
ASP.NET MVC Routing evaluates routes from top to bottom. So if two routes match, the first one it hits (the one closer to the 'top' of the RegisterRoutes method) will take precedence over the subsequent one.
With that in mind, you need to do two things to fix your problem:
Your default route should be at the bottom.
Your routes need to have constraints on them if they contain the same number of segments:
What's the difference between:
example.com/1
and
example.com/index
To the parser, they contain the same number of segments, and there's no differentiator, so it's going to hit the first route in the list that matches.
To fix that, you should make sure the routes that use ProductIds take constraints:
routes.MapRoute(
"TestRoute",
"{id}",
new { controller = "Product", action = "Index3", id = UrlParameter.Optional },
new { id = #"\d+" } //one or more digits only, no alphabetical characters
);
You don't need to start with Plugins for your route url. it is enough
to follow this pattern {controller}/{Action}/{parameter}
Make sure also namespace for the controller is correct as you define
in the routing. Nop.Plugin.Test.Pohoda.Controllers
You can define an optional productId parameter as well. so it will
work for mypage/TestPohoda/ImportProductInfo or
mypage/TestPohoda/ImportProductInfo/123
You can also set the priority higher than 0 which is priority of the
default routeprovider in the nop.web. this way you ensure that your
plugin reads it first. Indeed it is not necessary as you have
specific url. this is only required if you have similar route url
Try using this route
namespace Nop.Plugin.Test.Pohoda
{
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Plugin.Test.Pohoda.ImportProductInfo",
"TestPohoda/ImportProductInfo/{productId}",
new { controller = "TestPohoda", action = "ImportProductInfo" , productId = = UrlParameter.Optional },
new[] { "Nop.Plugin.Test.Pohoda.Controllers" }
);
}
public int Priority
{
get
{
return 1;
}
}
}
}
We will have a look at how to register plugin routes. ASP.NET routing is responsible for mapping incoming browser requests to particular MVC controller actions. You can find more information about routing here. So follow the next steps:
If you need to add some custom route, then create RouteProvider.cs file. It informs the nopCommerce system about plugin routes. For example, the following RouteProvider class adds a new route which can be accessed by opening your web browser and navigating to http://www.yourStore.com/Plugins/PaymentPayPalStandard/PDTHandler URL (used by PayPal plugin):
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute("Plugin.Payments.PayPalStandard.PDTHandler", "Plugins/PaymentPayPalStandard/PDTHandler",
new { controller = "PaymentPayPalStandard", action = "PDTHandler" });
}
public int Priority
{
get
{
return -1;
}
}
}
It could be cache problem, try to restart IIS
actually you do nota have to register route by default you can call your method
/TestPohoda/ImportProductInfo
I have a blog that I have built. It uses a web api in c# .NET.
If you click here: http://www.judsondesigns.com/api/blogapi/17
You will see it return an entry from the server. How can I easily rewrite the url to use the blog title instead of the ID?
So instead you can access it via: http://www.judsondesigns.com/api/blogapi/my_blog_tite_here
I have done this with isapi rewrites in the past on linux, but wasnt clear how to in .NET. I have heard different way but would like the less is more approach here. Thanks in advance. -Judson
What you want to do is create a custom RouteBase. This code review post is a good place to start.
The jist of it is:
public class MyRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// parse url and turn into route
}
public override VirtualPathData GetVirtualPath(
RequestContext requestContext,
RouteValueDictionary values)
{
// create url from route
}
}
Which you then register along with any other routes like
routes.Add(new MyRoute());
By editing the route configuration:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{blogTitle}",
defaults: new { controller = "Home", action = "Index", blogTitle = UrlParameter.Optional }
);
}
}
or rewrite the action to use the name instead and using a named parameter
http://www.judsondesigns.com/api/blogapi/?blogtitle=my_blog_tite_here
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'm new to MVC (and ASP.Net routing). I'm trying to map *.aspx to a controller called PageController.
routes.MapRoute(
"Page",
"{name}.aspx",
new { controller = "Page", action = "Index", id = "" }
);
Wouldn't the code above map *.aspx to PageController? When I run this and type in any .aspx page I get the following error:
The controller for path '/Page.aspx' could not be found or it does not implement the IController interface.
Parameter name: controllerType
Is there something I'm not doing here?
I just answered my own question. I had
the routes backwards (Default was
above page).
Yeah, you have to put all custom routes above the Default route.
So this brings up the next question...
how does the "Default" route match (I
assume they use regular expressions
here) the "Page" route?
The Default route matches based on what we call Convention over Configuration. Scott Guthrie explains it well in his first blog post on ASP.NET MVC. I recommend that you read through it and also his other posts. Keep in mind that these were posted based on the first CTP and the framework has changed. You can also find web cast on ASP.NET MVC on the asp.net site by Scott Hanselman.
http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx
http://www.asp.net/MVC/
I just answered my own question. I had the routes backwards (Default was above page). Below is the correct order. So this brings up the next question... how does the "Default" route match (I assume they use regular expressions here) the "Page" route?
routes.MapRoute(
"Page",
"{Name}.aspx",
new { controller = "Page", action = "Display", id = "" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
On one of Rob Conery's MVC Storefront screencasts, he encounters this exact issue. It's at around the 23 minute mark if you're interested.
Not sure how your controller looks, the error seems to be pointing to the fact that it can't find the controller. Did you inherit off of Controller after creating the PageController class? Is the PageController located in the Controllers directory?
Here is my route in the Global.asax.cs
routes.MapRoute(
"Page",
"{Page}.aspx",
new { controller = "Page", action = "Index", id = "" }
);
Here is my controller, which is located in the Controllers folder:
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class PageController : Controller
{
public void Index()
{
Response.Write("Page.aspx content.");
}
}
}
public class AspxRouteConstraint : IRouteConstraint
{
#region IRouteConstraint Members
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return values["aspx"].ToString().EndsWith(".aspx");
}
#endregion
}
register the route for all aspx
routes.MapRoute("all",
"{*aspx}",//catch all url
new { Controller = "Page", Action = "index" },
new AspxRouteConstraint() //return true when the url is end with ".aspx"
);
And you can test the routes by MvcRouteVisualizer