Mvc: Same ActionResult with different name depending url - asp.net-mvc

I would like to know what's the best way to achieve this.
I have an ActionResult in my controller, actually it has news name, now I need to internationlize my website and I can't have the same news name, it must to change depending the country where it's visited.
for example, now I need something like.
www.something.com/en/us/news for english version
www.something.com/co/es/noticias for spanish version
you have the point for the next countries.
I don't think I need to create x methods depending x urls that make exactly the same, but I don't know how to achieve it in a really efficient way ... thanks

You could create a new class TranslatedRoute and TranslationProvider to map the different translations to the same action. Then you can plug these into the routing system and override the default mapping.
Here's a good blog post which describes the idea: http://blog.maartenballiauw.be/post/2010/01/26/Translating-routes-%28ASPNET-MVC-and-Webforms%29.aspx

How does your routing work now? Maybe something like this answer would work, if you don't already use it. Perhaps something variation of this, with the parts of the URL in a different order, to suit your needs. For example, the controller doesn't necessarily have to come first in the route (or at all, in this case just always using the same controller name). Make some sort of map that gets you the word "news" in each different language, using the language code as a key.
// populate this map somewhere - language code to word for "news" (and any other name of the controllers that you have)
var newsControllerMap = new Dictionary<string, string>();
newsControllerMap["en"] = "news"; // etc.
// ...
// inside of the RouteConfig class (MVC 4) or RegisterRoutes() method in Global.asax.cs (MVC 3)
// just making an assumption that whatever class/entity you use ("LanguageAndCountry" in this case) also has a country code to make this easier. Obviously this would be refactored to have better naming/functionality to make sense and meet your needs.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
LanguageAndCountryRepository langRepo = new LanguageAndCountryRepository();
var languagesandCountries = langRepo.GetAllLanguagesWithCountries();
foreach (LanguageAndCountry langAndCountry in languagesandCountries)
{
routes.MapRoute(
"LocalizationNews_" + langAndCountry.LanguageAbbreviation,
langAndCountry.LanguageAbbreviation + "/" + langAndCountry.CountryCode + "/" + newsControllerMap[langAndCountry.LanguageAbbreviation],
new { lang = language.LanguageAbbreviation, country = langAndCountry.CountryCode, controller = "News", action = "Index"});
// map more routes to each controller you have, each controller having a corresponding map to the name of the controller in any given language
}

Related

Routing with dashes and non-english characters

I have a specific routing need that I can't get to work. I've found quite a few answers here on StackOverflow that takes me a bit on the way, but not all the way.
Im naming my controllers and actions in the standard C# way, i.e. the first letter of every word is uppercase:
MyController.MyAction()
To reach this action method, I'd like all of these urls to work:
/my-controller/my-action
/my-cöntroller/my-äction
/MyController/MyAction
/MyCöntroller/MyÄction
(the two last ones are not super important though...)
So there's two things here:
Dashes may be used to separate the words (for readability and SEO
purposes).
Some non-english characters can be used (all replacements
specified - no "magic").
I want to create the links with helpers like this:
#Html.ActionLink("My link text", "MyController", "MyAction")
i.e. the standard way, and this will create the following link:
/my-controller/my-action
Hopefully this could be done without making my routing configuration too messy (e.g. with one route for every action or something), or putting attributes on all action methods, but if thats the only solution I'd like to know.
What I've tried so far is implementing a custom route class overriding GetRouteData() and GetVirtualPath(). It got me closer but not all the way, but I might do something wrong
I had an idea for solving the problem with non-english characters by doing the replacement before the routing is performed, but I haven't found a way to do this yet (see this question).
I'd be really greatful if someone could help me with this, or at least point me in the right direction! :)
Edit: Note that the example urls above are just to describe what I want. In reality there is a lot of urls that must be handled, so I'd prefer some generic solution and not one involving one route for every action or something like that.
Create your resource file
Name Value
myaction1 my-äction
myaction2 my-action
mycontroller1 my-cöntroller
mycontroller2 my-controller
Add following routes in RegisterRoute method in global.asax
routes.MapRoute(
name: "route1",
url: Resources.Actions.mycontroller1 + "/" + Resources.Actions.myaction1 ,
defaults: new { controller = "mycontroller1", action = "myaction1" }
);
routes.MapRoute(
name: "route2",
url: Resources.Actions.mycontroller2 + "/" + Resources.Actions.myaction2,
defaults: new { controller = "mycontroller2", action = "myaction2" }
);
In controller,
[HttpGet]
public ActionResult myaction1()
{
return View("");
}
[HttpGet]
public ActionResult myaction2()
{
return View("");
}
In _Layout.cshtml
#Html.ActionLink(Resources.Actions.myaction1, "myaction1", "mycontroller1")
#Html.ActionLink(Resources.Actions.myaction2, "myaction2", "mycontroller2")

route to reflect hierarchical url/menu structure

i've created a basic mvc3 website whereby each controller represents the first folder in a url structure.
for example, the "food" and "drinks" folders below are controller. there are only two controllers which contain all of the sub-items in them.
ie in the first line of the example, controller=food, method=asian
in the second line controller=food, method=pad-thai and so on and so forth.
www.mysite.com/food/asian/
www.mysite.com/food/asian/pad-thai
www.mysite.com/food/italian/chicken-parmigiana
www.mysite.com/drinks/cocktails/bloody-mary
how would i write routes so that www.mysite.com/food/asian/pad-thai will direct to the food controller and the paid thai method within that controller, and also have a rule to send from www.mysite.com/food/asian/ to the food controller and asian index method??
The MVC design pattern isn't for rewriting URLs to point to folder structures. It can do this but it certainly isn't its main purpose. If you're trying to create a URL structure with static content, it might be easier to use the URL rewriting functionality built into IIS.
If you're creating a full MVC application, set up FoodController and DrinkController to serve up your views, for example:
public class FoodController : Controller
{
public ActionResult ViewDishByTag(string itemType, string itemTag)
{
// If an itemType is displayed without itemTag, return an 'index' list of possible dishes...
// Alternatively, either return a "static" view of your page, e.g.
if (itemTag== "pad-thai")
return View("PadThai"); // where PadThai is a view in your shared views folder
// Alternatively, look up the dish information in a database and bind return it to the view
return ("RecipeView", myRepo.GetDishByTag(itemTag));
}
}
Using the example above, your route might look a little like this:
routes.MapRoute(
"myRoute",
"{controller}/{itemType}/{itemTag}",
new
{
controller = UrlParameter.Required,
action = "ViewDishByTag",
itemtype = UrlParameter.Optional,
itemTag = UrlParameter.Optional
}
);
Your question doesn't contain much detail about your implementation, so if you'd like me to expand on anything, please update your question.

Routing with sub domains

I have an MVC website which has 3 main components
Member area which has the path /Member/{controller}/{action}/{id}
Individual pages which will respond to any subdomain, e.g. user1.example.com/{controller}/{action}/{id}, user2.example.com/{controller}/{action}/{id}
Main website which will respond to any url under www.example.com/{controller}/{action}/{id}
What is the easiest way to handle the routes that will allow the above 3 items to co-exist? I have tried many MapRoutes from the global.asax.cs file and also making a new class based on RouteBase but not having much luck.
It sounds like you're heading in the right direction - essentially you need to create a custom route which looks at the request and constructs the route value dictionary. Rather than reinvent the wheel though, someone has already created a nice implementation which allows you to include placeholders in the domain itself like so:
routes.Add("DomainRoute", new DomainRoute(
"{controller}.example.com", "{action}/{id}",
new { controller = "Home", action = "Index", id = "" }));
http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx

Refactoring my route to be more dynamic

Currently my URL structure is like this:
www.example.com/honda/
www.example.com/honda/add
www.example.com/honda/29343
I have a controller named HondaController.
Now I want to refactor this so I can support more car manufacturers.
The database has a table that stores all the manufacturers that I want to support.
How can I keep my URL like above, but now support:
www.example.com/ford
www.example.com/toyota/add
etc.
I can easily rename the HondaController to CarController, and just pass in the string 'honda' or 'toyota' and my controller will work (it is hard coded to 'honda' right now).
Is this possible? I'm not sure how how to make a route dynamic based on what I have in the database.
Any part of your route can be dynamic just be making it into a route parameter. So instead of "/honda/{action}", do:
/{manufacturer}/{action}
This will give you a parameter called "manufacturer" that was passed to your action method. So your action method signature could now be:
public ActionResult add(string manufacturer) { }
It would be up to you to verify that the manufacturer parameter correctly matched the list of manufacturers in the database - it would probably be best to cache this list for a quicker lookup.
Updated: What I mean by "you have to take out the default parameters" for the default route is this. If you have:
route.MapRoute("Default", "/{controller}/{action}/{id}",
new { id = 1 } // <-- this is the parameter default
);
then this route will match any url with two segments, as well as any url with three segments. So "/product/add/1" will be handled by this route, but so will "/product/add".
If you take out the "new { id = 1 }" part, it will only handle URL's that look like "/product/add/1".
i have made something like this for granite as i wanted to have a material controller but have a url like so:
black/granite/worktops
black/quartz/worktops
etc
i did this route:
routes.MapRoute("Quote", "quote/{color}/{surface}/{type}",
new {controller = "Quote", action = "surface"});
swap quote for car so u can have:
car/honda/accord
your route can then be
routes.MapRoute("cars", "car/{make}/{model}",
new {controller = "Cars", action = "Index"});
your actionResults can then look like this:
public ActionResult Index(string make, string model)
{
//logic here to get where make and model
return View();
}
that i think covers it
What I recommend is instead using:
domain/m/<manufacturer>/<action>
Where 'm' is the manufacturer controller. This will allow you to use the same controller for all of your extensions and save you a lot of headache in the future, especially when adding new features. Using a one-letter controller is often times desirable when you want to retain your first variable ( in this case) as the first point of interest.

Areas And Routes

I'm using areas everywhere and I'm wanting something like the following:
http://localhost/MyArea/MySection/MySubSection/Delete/20
Usually I access things by doing the following:
http://localhost/MyArea/MySection/MySubSection/20
But if I want to delete then I have to say
http://localhost/MyArea/MySection/DeleteEntryFromMySubSection/20
With routes, how do you do this? (the routes aren't realistic by the way, they're much more concise than this in my system)
EDIT: This is specifically related to the use of Areas, an ASP.NET MVC 2 Preview 2 feature.
It would depend on how your routes & controllers are currently structured.
Here's an example route you might want to use.
If you want to be able to call the following route to delete:
http://localhost/MyArea/MySection/MySubSection/Delete/20
And let's assume you have a controller called "MyAreaController", with an action of "Delete", and for the sake of simplicity let's assume section and subsection are just strings e.g.:
public class MyAreaController : Controller
{
public ActionResult Delete(string section, string subsection, long id)
{
Then you could create a route in the following way (in your Global.asax.cs, or wherever you define your routes):
var defaultParameters = new {controller = "Home", action = "Index", id = ""};
routes.MapRoute("DeleteEntryFromMySubSection", // Route name - but you may want to change this if it's used for edit etc.
"{controller}/{section}/{subsection}/{action}/{id}", // URL with parameters
defaultParameters // Parameter defaults
);
Note: I'd normally define enums for all the possible parameter values. Then the params can be of the appropriate enum type, and you can still use strings in your path. E.g. You could have a "Section" enum that has a "MySection" value.

Resources