Breadcrumb not showing on culture change - asp.net-mvc

Disclaimer : I'm still a rookie, and I'm maintaining a big project made by someone else with little documentation. So I might be missing something obvious.
Here goes :
My web application can be accessed by 2 dns names, "www.website.com" and "www.siteweb.com" one in english and the other in french.
There used to be a bilingual splash page that prompts for the language but since we have 2 urls, I had to eliminate the splash page and automatically redirect to the main page in the language that corresponds to the URL that was used to access it.
Here's is the redirection code located on the defunct splash page (index.vbhtml) :
#Code
If Request.ServerVariables("SERVER_NAME").Contains(ConfigurationManager.AppSettings("SiteDNSnameEN")) Then
Response.Redirect(Url.Action("SetLanguage", Resources.ResourceURL.CONTROLLER_LANGUAGE, New With {.pCulture = "en-CA", .pReturnURL = "/Home"}))
ElseIf Request.ServerVariables("SERVER_NAME").Contains(ConfigurationManager.AppSettings("SiteDNSnameFR")) Then
Response.Redirect(Url.Action("SetLanguage", Resources.ResourceURL.CONTROLLER_LANGUAGE, New With {.pCulture = "fr-CA", .pReturnURL = "/Accueil"}))
End If
#EndCode
When on the French "Home" page, the breadcrumb works fine when I use the language toggle button, which refers to a controller that translates the url and triggers "SetLanguage" which basically sets CurrentCulture and CurrentUICulture and reloads the pReturnURL. Everything gets translated properly and there's no problem.
But if I use the English URL (or fake using it, because in dev it's only localhost), the breadcrumb is displayed , but when I press on the language toggle button, the breadcrumb is not being displayed. My best guess is that MVCSiteMapProvider is still looking for the english ressources because the breadcrumb gets displayed on that one page that happens to have the same path in french or english.
I have played around with globac.asax's routes and with the redirecting, but I only achieve to fix one language and screw the other.
I'm sure there is a just a small fix that have been eluding me for quite a while now. Somewhere along the line, the culture change seems to not get done. I've troubleshooted the Action "SetLanguage" and it always shows the expected culture. That might not be it but I'm thinking MVCSiteMapProvider is not always following for some reason.
Here's the part of Global.asax that I've tinkered with :
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
routes.MapRoute( _
"Splash", _
"", _
New With {.controller = "Root", .action = "Index"} _
)
routes.MapTranslatedRoute( _
"TranslatedRoute", _
"{controller}/{action}/{id}", _
New With {.controller = "Home", .action = "Index", .id = ""}, _
New With {.controller = translationProvider, .action = translationProvider}, _
False _
)
Dim controllerHomeEN = Resources.ResourceURL.ResourceManager.GetString("CONTROLLER_HOME", New CultureInfo(WebHelper.CONST_CURRENT_CULTURE_EN))
Dim actionIndexEN = Resources.ResourceURL.ResourceManager.GetString("ACTION_INDEX", New CultureInfo(WebHelper.CONST_CURRENT_CULTURE_EN))
routes.MapRoute( _
"Default", _
"{controller}/{action}/{id}", _
New With {.controller = controllerHomeEN, .action = actionIndexEN, .id = UrlParameter.Optional} _
)

Usually, when the breadcrumb disappears it means that you are not accounting for all of the request's route values in the node. For example, if you have a "culture" route value being passed from the route and you don't have it setup in preservedRouteParameters, then the node won't match the request.
Best guess is that you are doing something with your translationProvider that is causing the node not to match. But since you haven't posted the code, I can only imagine what.
Note that if you are writing to the SiteMapNode.Title property somewhere you shouldn't be, the value could bleed over to other requests. MvcSiteMapProvider v3.x was not threadsafe, so if one request updated a value in the SiteMap's cached nodes, they would be visible in all of the other requests until the cache expires (or until the next request overwrote it).
Do note that dynamic node providers are not dynamically called per request. They are for creating cached (shared) nodes that are based on dynamic data. Therefore, you should not put any per-request conditions (such as localization) inside of a dynamic node provider.
The How to Localize Site-Map Data document on MSDN shows the correct way to configure localization in MvcSiteMapProvider v3.x. Do note that there is an issue with deployment of global resources within MVC when using the default file build settings.
Also see:
SiteMapPath empty when changing culture #359
Localized Site Map does not work after switching Culture #52
Sometimes the bredcrumbs is not shown. #331
breadcrumbs not appearing for optional route paramters #387
For MvcSiteMapProvider v4.x, you have other options if you were to upgrade. Also, MvcSiteMapProvider v4.x is threadsafe - if you update a value such as Title within the request, it will apply only to the current request and no other user will see it.

Related

Default route problem

If I want to hit a url like
http://localhost:8080/controllername
I want the "Index" action to be the default action called. I assumed the default route mapping would be fine and the "Index" action would be called on whatever controller was specified - seems I need to specify
http://localhost:8080/controllername/index
Is this correct?
Mapping:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
What you're trying should definitely work. In fact, the code you posted is from the default templates, and I've just tested it by adding an "Index" action to the AccountController and visiting /Account in my browser.
I'd recommend creating a new project and testing this behaviour (first with the built-in web server, then with IIS, if you're not always using the built-in server). If it works, there's probably something different in your project that's causing the issue.
I had a similar problem and it occured because of a collision with a directory in the project. I had a structure like this in my project:
Controllers \
HomeController.cs
CmsController.cs
Cms \
WhateverFile.cs
The Cms subdirectory collided with the /Cms URL, while Cms/Index worked. I simply renamed my colliding folder name. If you have to keep it, there is a RouteCollection.RouteExistingFiles that can be used to prevent automatic lookup of files. If that is enabled I think that a lot exclusions have to be added for the Script etc, see this blog post for an example.

Troubles creating URLs in a VB.NET MVC view

I'm trying to write out animals from a database. All the samples I see for creating View pages have hard-coded "nodes" in the URLs. I can get my first view to work but my second one doesn't write out the correct URL to go to the third one. So, for example, I want my URLs to be:
/animals/
/animals/canines/
/animals/canines/schnauzer
I have the default route setup:
> routes.MapRoute( _
> "Default", _
> "{controller}/{action}/{id}", _
> New With {.controller = "Home", .action = "Index", .id =
> UrlParameter.Optional} _
> )
The first URL works great and I can list the animal groups on the first one along with the correct links to the second URLs. I do that as follows:
for each animal in model
Html.ActionLink(animal.animal_name,animal.animal_name.Trim)
next
I also can get the canine groups written out on the second (so I know my model is working), however the URL.Action on that second "canines" page loses the "canines" in the URL so it renders as:
/animals/schnauzer
I've tried every way I can think of to write out the link including:
<a href="<%= url.action(canine.dog_name.Trim)) %>">
<a href="<%= Url.Content("~" & url.action(canine.dog_name.Trim)) %>">
and a few others that aren't worth showing. ;-D
... I'm guessing I'm missing something in a route path but what is it? It can't be that I have to know the path I'm at on every page in order to write out the URL - can it?
Thanks in advance for your help!
The route youve configured looks for three components, a controller, action and id.
In your case /animals/schnauzer, you're looking for more than a single route element as far as HTTP routing (and link generation), is concerned. It sees /animals/canines as two different values.
If you're looking to allow a route like http://yoursite/Family/Species, specify it as such:
routes.mapRoute("familyspecies", "animals/{family}/{species}", new { controller = "Home", action = "Animals", Species = UrlParameter.Optional});
and pass family and species in to your actions
public ActionResult Animals(string family, string species){...}
OR
Use wildcard routes to direct everything after the action to the id
routes.MapRoute("{controller}/{action}/{*id}");

I'm getting a "Does not implement IController" error on images and robots.txt in MVC2

I'm getting a strange error on my webserver for seemingly every file but the .aspx files.
Here is an example. Just replace '/robots.txt' with any .jpg name or .gif or whatever and you'll get the idea:
The controller for path '/robots.txt'
was not found or does not implement
IController.
I'm sure it's something to do with how I've setup routing but I'm not sure what exactly I need to do about it.
Also, this is a mixed MVC and WebForms site, if that makes a difference.
You can ignore robots.txt and all the aspx pages in your routing.
routes.IgnoreRoute("{*allaspx}", new {allaspx=#".*\.aspx(/.*)?"});
routes.IgnoreRoute("{*robotstxt}", new {robotstxt=#"(.*/)?robots.txt(/.*)?"});
You might want to ignore the favicon too.
routes.IgnoreRoute("{*favicon}", new {favicon=#"(.*/)?favicon.ico(/.*)?"});
You can adjust the regular expression to exclude paths.
Haacked from the source.
The ignore route given above didn't work for me but I found a similar one that did:
routes.IgnoreRoute("{*staticfile}", new { staticfile = #".*\.(css|js|gif|jpg)(/.*)?" });
This error could also happen if inside a view in your area, you use the Html.Action helper. This helper will always use the area as a prepend, unless you specifically tell it not to. E.g.,
#Html.Action("Main", "Navigation", new { area = string.Empty })
I found another solution too... While I don't think I'll use it, it's worth showing here in the answers:
The following should (in theory) ignore looking for controllers for anything with a '.' in it.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" }, // Parameter defaults
new { controller = #"[^\.]*" } // Parameter contraints.
);
Do you still have:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
... in your Global.asax.cs?
MVC puts it there by default, and it's supposed to handle this.
If you do, then the problem may be how you're mixing MVC and WebForms.
I encountered this error when I request resources that did not exist.
Specifically, I was requesting a custom IE css file:
<!--[if lt IE 8]>#Styles.Render("~/Content/ie7.css")<![endif]-->
(These are condition comments, interpreted by IE)
However, the actual resource existed on ~/Content/ie/ie7.css.
So, without any modifications to the routing, the error was solved by using the correct url of the resource.

Strange route problem in ASP.NET MVC - default route not hit

I have this two routes currently in my application after decommenting out many other ones. Let me first explain that I have quite a big application already but have come to a problem where my application does not start at the root url anymore.
If I set starting page to default.aspx then webapp starts at (example) http://localhost:55421/Default.aspx. I don't want that. I want it without Default.aspx
So I went into app properties and removed Default.aspx as starting page - now it is blank field (just like in a sample new MVC app if you create it in VS 2008).
But now application does start at the required URL but issues an error:
"The incoming request does not match any route."
Also If I use route debugger it also misses all routes and catches it by catchall route.
I don't know how all of this is possible since as I said above I have two default routes configured at this time:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Pages", action = "Display", slug = "Default" }
);
Any help appreciated
Am I right in thinking you are trying to hit
http://server/{controller}/{action}/{id}
with
http://server/
If you are I think you need to provide a default for the last parameter {id}. You have a default for a parameter slug but without a default for {id} I don't think ASP.NET Routing can hit it.
If I'm right
http://server/Pages/Display
should also not hit the default route, because you are expecting id in Display?
HTH
Alex

How do you set the startup page for debugging in an ASP.NET MVC application?

How do you start debugging the application at the application root? For example: http://localhost:49742/
I'm always getting a page which doesn't exist, such as:
http://localhost:49742/Views/Home/About.aspx
Note that it would be OK to start at http://localhost:49742/Views/Home/About
Go to your project's properties and set the start page property.
Go to the project's Properties
Go to the Web tab
Select the Specific Page radio button
Type in the desired url in the Specific Page text box
While you can have a default page in the MVC project, the more conventional implementation for a default view would be to use a default controller, implememented in the global.asax, through the 'RegisterRoutes(...)' method. For instance if you wanted your Public\Home controller to be your default route/view, the code would be:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Public", action = "Home", id = UrlParameter.Optional } // Parameter defaults
);
}
For this to be functional, you are required to have have a set Start Page in the project.
This works for me under Specific Page for MVC:
/Home/Index
Update: Currently, I just use a forward slash in the "Specific Page" textbox, and it takes me to the home page as defined in the routing:
/
Selecting a specific page from Project properties does not solve my problem.
In MVC 4 open App_Start/RouteConfig.cs
For example, if you want to change startup page to Login:
routes.MapRoute(
"Default", // Route name
"", // URL with parameters
new { controller = "Account", action = "Login"} // Parameter defaults
);
If you want to start at the "application root" as you describe right click on the top level Default.aspx page and choose set as start page. Hit F5 and you're done.
If you want to start at a different controller action see Mark's answer.
Revisiting this page and I have more information to share with others.
Debugging environment (using Visual Studio)
1a) Stephen Walter's link to set the startup page on MVC using the project properties is only applicable when you are debugging your MVC application.
1b) Right mouse click on the .aspx page in Solution Explorer and select the "Set As Start Page" behaves the same.
Note: in both the above cases, the startup page setting is only recognised by your Visual Studio Development Server. It is not recognised by your deployed server.
Deployed environment
2a) To set the startup page, assuming that you have not change any of the default routings, change the content of /Views/Home/Index.aspx to do a "Server.Transfer" or a "Response.Redirect" to your desired page.
2b) Change your default routing in your global.asax.cs to your desired page.
Are there any other options that the readers are aware of? Which of the above (including your own option) would be your preferred solution (and please share with us why)?

Resources