I'm trying to plan for future (months away) localization of a new ASP.NET MVC site.
Trying to decide what makes most sense to do, as far as constructing the URLs and routing.
For instance should I start off immediately with this :
http://www.example.com/en/Products/1001
http://www.example.com/es/Products/1001
or just
http://www.example.com/Products/1001
and then add other languages later
http://www.example.com/en/Products/1001
Thats my basic main issue right now, trying to get the routing correct. I want my URLs to be indexable by a search engine correctly. I'm not even sure if I want language in the URL but I dont think there is a good alternative that wouldn't confuse a search engine.
It leads to all kinds of other questions like 'shouldnt I localize the word products' but for right now I just want to get the routing in place before I launch the english site.
I have exactly the same URL_mapping like you. My route also uses a constraint.
Works for me.
routes.MapRoute(
// Route name
"LocalizedController",
// URL with parameters
"{language}/{controller}/{action}",
// Parameter defaults
new {
controller = "Home", action = "Index",
language = "de"
},
//Parameter constraints
new { language = #"de|en" }
I would use a different URL scheme like so :
en.mysite.com (English)
mysite.com (default language)
ro.mysite.com (Romanian)
etc.
Then I would create a custom route like in this answer.
Related
I have a Sitecore site and on 2 of my CD servers, Url.Action returns an empty string. This works locally and on 9 other servers ranging from dev to prod, CD and CM.
Deployment automation ensures that the exact same web.config is deployed to all environments; ditto for all other configs.
My controller inherits from SitecoreController properly. This is not isolated to a certain controller or action, this happens with all controllers and actions.
What would make Url.Action return an empty string in one environment and not others, with identical code?
What would make Url.Action return an empty string sometimes?
Specifically, route values that are derived from the current request.
Explanation
The Url.Action method is driven by the UrlHelper, which in turn is driven by routes. It uses route values to determine which route to use to build the URL. The routing framework attempts to match each route against the route values in the order they are registered until a match is found. If the routing framework reaches the end of the routing table and there is still no match, it returns an empty string (because there is no other reasonable default behavior).
On the other hand, if you call Url.Action and pass a route name, this narrows the possible matches to only 1 specific route (the named one). But the route values still need to match that route or you get the default empty string.
In general, all route values must match, but there are a couple of things that may make the behavior quirky:
Route values can be made optional. This means that the route value doesn't need to be present in order for the route to match.
If a route value is not supplied in the call to Url.Action, it may be supplied automatically if it exists in the current request.
This second quirk means that if Url.Action is put on a shared view and one request contains a route value to make it match a route, and another request doesn't contain that route value, in the latter case the URL may match another route or it may be an empty string.
Example
Say the routing configuration is setup like this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "AboutWith4RouteValues",
url: "test/home/about/{foo}/{bar}",
defaults: new { controller = "Home", action = "About" });
routes.MapRoute(
name: "ContactWith4RouteValues",
url: "test/home/contact/{foo}/{bar}",
defaults: new { controller = "Home", action = "Contact", bar = UrlParameter.Optional });
routes.MapRoute(
name: "Home",
url: "",
defaults: new { controller = "Home", action = "Index" }
);
}
}
Also, let's say there is a link on the _Layout.cshtml page:
<a href='#Url.Action("About", "Home")'>About</a>
If you go to the home page in the browser (/), the resulting link URL will be:
<a>About</a>
This is because foo and bar are not present in the route values of the request so it doesn't match any of the registered routes.
Actually, Url.Action returns an empty string, but Razor optimizes away the empty href='' attribute.
On the other hand, if you put this URL in the browser:
/test/home/contact/arg1/arg2
The link URL is generated as:
<a href='/test/home/about/arg1/arg2'>About</a>
This is because both {foo} (with value arg1) and {bar} (with value arg2) are available in the current request. Note that the incoming request matches the ContactWith4RouteValues route, but when the link URL is generated it uses the AboutWith4RouteValues route. Since both foo and bar are present in the request, they are carried over to the generation of the URL.
Now, if the URL in the browser is changed to:
/test/home/contact/arg1
It still matches the ContactWith4RouteValues route because the last value is optional. However, the URL that is generated is now:
<a>About</a>
This is because foo has a value in the request, but bar has no value, the Url.Action generation request does not match AboutWith4RouteValues because bar is a required value in order to make it match. And since it also doesn't match the Home route, we have reached the end of the route table and the only logical thing to return is empty string.
Workaround
The simplest workaround to avoid these quirks of the current request is to manually specify the route values when calling Url.Action or other UrlHelper based methods (such as ActionLink, RedirectToRoute, etc).
<a href='#Url.Action("About", "Home", new { foo = Model.Foo, bar = Model.Bar })'>About</a>
This ensures those values are always present when building the URL even if they don't happen to be part of the current request.
I had this exact same issue and for me the issue had to do with Case-Sensitivity in my Routing.
Deploying the same site to two different Applications on the same IIS Web Server.
I used the same web-config and same VS Web Publish Settings.
Yet, my Url.Action was returning blanks (empty-strings) in Prod, but not in Dev.
After reviewing the code, something caught my eye.
I have an Area called WorkBench (upper-case "B").
In a few places (where Url.Action returned blank), I was passing in Workbench (lower-case "b").
I have some fancy logic in my RouteConfig.cs and Area Registration (i.e. WorkBenchAreaRegistration.cs).
In those files, I had Conditional Logic to determine what Environment the Application was running in.
I use the same Project to share code, but only want some Areas accessible in different Environments.
Even though Prod and Dev are different Environments, to debug this issue,
I altered them so they would temporarily appear the same.
The issue was fixed once I capitalized the "b" to "B" everywhere.
I still do not know why it would work on my local machine and in Dev, but not in Prod.
It should have behaved the same way in both Environments.
Again, same server, same publish, same web.config, same iis config, same application code, etc...
Sorry I don't have an explanation for this, but at least this is a possible fix you could try.
Hope this helps someone out there.
Update 02/08/2019: I had this problem again with a different link and for this one I realized I had an option set to access all Actions within an Area when one of my Debug variables was set to true, or when running in Prod. I removed this conditional logic in my AreaRegistration.cs file and it fixed the problem when running in a Staging envrionment.
The Lesson Learned here is that anytime you see a blank Href, you gotta check those Route Configs.
Yesterday we had a Play 2.0 presentation at our local JUG but we couldn't figure out whether it is possible to have localized URLs (for SEO purposes).
For example /help, /hilfe etc should point to the same controller but the template should be rendered with different language content.
Is there any way to do this in Play 2.0?
I like your question, because it was creative at least for me :) Check this approach it works for me:
conf/routes:
GET /help controllers.Application.helpIndex(lang = "en")
GET /hilfe controllers.Application.helpIndex(lang = "de")
GET /help/:id controllers.Application.helpTopic(lang = "en", id: Long)
GET /hilfe/:id controllers.Application.helpTopic(lang = "de", id: Long)
controllers/Application.java:
public static Result helpIndex(String lang) {
return ok("Display help's index in " + lang.toUpperCase());
}
public static Result helpTopic(String lang, Long id) {
return ok("Display details of help topic no " + id + " in " + lang.toUpperCase());
}
views/someView.scala.html:
Help index<br/>
Hilfe index<br/>
Help topic no 12<br/>
Hilfe topic no 12
(This is different approach than in previous answer, therefore added as separate one)
You can also create some kind of mapping table in DB where you can store full paths to records with different params:
urlpath record_id lang
/help/some-topic 12 en
/hilfe/ein-topic 12 de
than in conf/routes file you need to use rule allowing you to use Dynamic parts spanning several / (see routing doc) ie:
GET /:topic controller.Application.customDbRouter(topic:String)
You can also mix both - standard routing mechanismus with custom one by placing above rule at the end of your conf/routes file if no 'static' rule will be available, then it will try to find it in mapping table or will return notFound() Result.
You use from GlobalSettings.onHandlerNotFound() and check if the is a translated version of the url. Then you can make a redirect. However this ends with urls in default language.
More cleaner would be to use the GlobalSettings.onRouteRequest where you can implement your own logic to get the handler.
Furthermore you can create your own router. There was a discussion about it at google-groups with a scala solution.
It was possible in Play 1.2.x, not in 2.x as far as I know. I mean, it's not possible without duplicating the mappings in your file, adding one for EN, one for DE, etc.
A simpler alternative for SEO may be to "fake" the urls in your Sitemaps file.
So your Routes file has
GET /action/:param/:seo-string Controller.methodAction(param)
so seo-string will be ignored in processing, and you generate several links on your Sitemaps file:
/action/1/english-text
/action/1/german-text
This would set the search engines. For users, so they see the url in the right language, you could change the URL using HTML 5 history.
It's extra work, but if you really want it...
To support legacy URLs in my application, I use a regex to convert URLs of the form /Repo/{ixRepo}/{sSlug}/{sAction} to the new form /Repo/{sName}/{sAction}, using the ixRepo to get the correct sName. This works well, and I can redirect the user to the new URL with a RedirectResult.
However, I'd like to catch legacy URLs with an invalid action before I redirect the user. How can I verify if a URL string will map to a registered route? MVC clearly does this internally to map a request to the correct action, but I'd like to do it by hand.
So far, I've come up with this:
var rd = Url.RouteCollection.GetRouteData(new HttpContextWrapper(new HttpContext(
new HttpRequest("", newPath, ""),
new HttpResponse(null))));
which appears to always return a System.Web.Routing.RouteData, even for bad routes. I can't find a way to check if the route was accepted as a catch all, or if actually mapping to a route that's registered on the controller.
How can I use MVC's routing system to check if a URL maps to a valid controller/action via a registered route?
(I've seen ASP.NET MVC - Verify the Existence of a Route, but that's really inelegant. MVC has a routing system built in, and I'd like to use that.)
Wrong question. Anything can be a route, whether or not it actually maps to an action.
I think you're asking, "Will this execute OK, or will it 404?" That's a different question.
For that, you need to do what MVC does. Look in the MVC source at MvcHandler.ProcessRequestInit and then ControllerActionInvoker.InvokeAction to see how MVC looks up the controller and action, respectively.
If you know the controller and ask for valid actions, just do some reflection stuff as done in here.
If the redirected url goes to your application, then you can check if the url goes to a valid route. Some code on haacked.com http://haacked.com/archive/2007/12/17/testing-routes-in-asp.net-mvc.aspx does route testing as a unit test. After this you have controller and action as routedata and you have to do, what Craig said "do the same as mvc does".
The routing system maps request uris to route handler. The mvc route handler (class) throws an exception if it fails. There is no checking.
You can add constraints to your routes. If you constrain the action property. Then checking if the url goes to a valid route my be what you want.
Please help I want to use first URI segment into my CodeIgniter website.
Like when I open these url they opens my profile:
http://www.facebook.com/buddyforever
or
http://www.myspace.com/zarpio
How can I do this with CodeIgniter? I checked _remap function but first coming controller how to hide controller?
You can do this using the URL routing of codeigniter...
If you want your URL to be http://www.mydomain.com/zarpio and you want it to refer to your_controller, then do the following.
/config/routes.php
$route['(.*)'] = "your_controller/$1"; // Now, `zarpio` will be passed to `your_controller`
You can access it in your controller like this...
$my_name = $this->uri->rsegment(2);
However I do not suggest this way of configuring URLs. A better way would be...
$route['users/(.*)'] = "your_controller/$1";
This way, you're just renaming your controller name your_controller to users.
If you want to access profile of a user, you can do it like this...
$route['users/profile/(.*)'] = "another_controller/method/$1";
$route['users/(.*)'] = "your_controller/$1";
Consider the order of routing. Since you wrote users/(.*) in your route, it will match users/zarpio as well as users/profile/zarpio, and route both of them to your_controller/$1, which in the case of profile will give you a 404 page not found error. That is why you need to write users/profile/(.*) before users/(.*) in your routing configuration.
More information in codeigniter URI class documentation
I am unsure if my title was accurate enough. I am trying to make SEO URLs for my website which is developed in ASP.NET MVC. I configured my route to include:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}/{seo}", // URL with parameters, SEO will up completely optional and used for strings to provide Search Engine Optimization.
new { controller = "Home", action = "Index", id = "", seo ="" } // Parameter defaults
);
On my development machine, a link like:
http://localhost:1048/Home/Post/96/Firefighting+ATV+Concept+Twin+Water+Cannons+Gull
works fine, but once I published to the server (Windows 2008 R2 IIS), it doesn't work. For example, the link:
http://www.otakuwire.net/Home/Post/96/Firefighting+ATV+Concept+Twin+Water+Cannons+Gull
gives me a 404.
Is this a routing issue, or some other issue?
This is an IIS issue, not routing. IIS7 is strict in how it deals with the plus symbol + in URLs. The easy fix is to use the dash - instead like everyone else, or be bold and use the space character (which, personal note, looks horrible in the IE address bar).
On ServerFault they present a configuration-based solution to allow + symbols:
https://serverfault.com/questions/76013/iis6-vs-iis7-and-iis7-5-handling-urls-with-plus-sign-in-base-not-querystri