Troubles creating URLs in a VB.NET MVC view - asp.net-mvc

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}");

Related

Breadcrumb not showing on culture change

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.

Get url for custom route in asp.net mvc

In my ASP.Net mvc application I have changed the default route registration in startup.cs to
routes.MapRoute(
name: "default",
template: "{customer}/{language=fi-FI}/{controller=Home}/{action=Index}/{id?}");
So all routes would contain the customer id and the current language. Custom segment matching takes care of putting the customer and language info in new urls so when the requested url is /customer1/fi-fi/somecontroller/someaction any generated url like this
<a asp-controller="othercontroller" asp-action="otheraction">Some action</a>
would be generated correctly with the customer and language code in it.
The question is, how should I generate the url so I can specify the customer and language code without having to do string concatenation? I would need this e.g. in the links for changing language.
I have tried
<a asp-controller="somecontroller" asp-action="someaction" asp-route="default" asp-route-customer="customer2" asp-route-language="en-us">Some action in other language and other customer</a>
but that says that I cannot specify action and controller when asp-route is defined and if I remote asp-route then nothing changes.
I got this working by using #Url.RouteUrl, e.g.
#Url.RouteUrl("default", new { customer = "someothercustomer", language = "someotherlanguage", controller = "somecontroller", action = "someaction" })

T4MVC #Url.Action(MVC.Controller.Action()) Renders "?Area=" Parameter in QueryString

I am rendering a menu from a Partial Action directly to the layout, using:
#Html.Action(MVC.Menu.Index())
This action, determines which Menu partial to render. For instance, a public menu partial. Within these partials, I am also using T4MVC to render the links:
<ul id="navHolder">
<li class="level1">
<ul class="mainMenu">
<li><b>#Html.ActionLink("Welcome", MVC.Home.Index())</b>
...
For some reason, the Urls rendered by T4MVC include "?Area=" at the end:
<ul id="navHolder">
<li class="level1">
<ul class="mainMenu">
<li><b>Welcome</b>
...
I have NO areas in my project and I have turned the "IncludeAreasToken" setting to false. Oddly, this only happens if I render the partial using "#Html.Action" -- if I pull it in as "#Html.Partial" the parameter isn't rendered and the link is clean and correct. (I don't want to render it as a partial though, so please don't offer that as a suggestion ;)
Anyone out there run into this before?
I solve this issue in a very easy way, simply by adding to all routes that are not in area empty area route like this:
routes.MapRoute(
"Default",
"{controller}/{action}/{i​d}",
new { controller = "Home", action = "Index", area = "", id = UrlParameter.Optional });
Something strange is going on here, and I wonder if there is some kind of MVC bug at the root. Even without using T4MVC, this happens if you write:
#Html.ActionLink("Welcome", "Index", "Home", new { Area = "" }, null)
In a regular view, this doesn't generate the bogus ?Area=, while in a Html.Action call it does. I need to ask someone on the team.
For now, you can workaround by deleting this line (around line 310) in t4mvc.tt:
<# if (MvcVersion >= 2) { #>result.RouteValueDictionary.Add("Area", area ?? "");<# } #>
Copied from workitem 7154 comments is a solution provided by #DavidEbbo:
A simpler workaround is to add a bogus area to your site. e.g.\n\n-
Right click Project and choose Add / Area. Name it 'Dummy' (or
whatever)\n- You can delete everything in there except for the
DummyAreaRegistration.cs file
Make sure you have the AreaRegistration.RegisterAllAreas(); call in your Global.asax
This also works with attribute routing in place.

How do I use an ActionLink with the RouteValueDictionary?

I'm trying to make my URL look like this:
http://domain.com/controller/action/123/SomeTextForSEO
I tried using an Action Link, but that appends ?company=SomeTextForSEO instead of the company name after a slash.
<%: Html.ActionLink("DomainList", "Index", "DomainList", new { id = item.CompanyID, company = item.CompanyDisplayName.Trim() }, new object { })%>
and now I think I need to use a RouteValueDictionary but I'm unsure of how to do this in ASP.NET syntax. Can someone point me in the right direction? The MSDN examples are insufficient.
If you go to global.asax.cs and add a new route similar to the existing default route with the pattern
"{controller}/{action}/{id}/{company}"
Before the default one, you should find that the link will generate correctly with your ActionLink call as-is.
I'm on a phone so I'd like to be more verbose (a route restriction would be a good idea to prevent this route interfering with the default), but the HTC keyboard is not code friendly ;)

Running ASP.NET MVC in a subdomain makes Html.ActionLink render broken links

I am running MVC in a subdomain
http://test.domain.com which points to the /Test directory on my webhost4life account.
Html.ActionLink("About", "About", "Home")
it renders a link to
http://test.domain.com/Test/Home/About -- which gives a 404
the link should be ..
http://test.domain.com/Home/About
is there a way to override ActionLink to omit the /Test on render?
Thank you
Experiment 1
I added a route to the table like this...
routes.MapRoute(
"Test", // Route name
"Test/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
and now action link renders links like this..
http://test.domain.com/Test/Test/Home/About/
when this is clicked it does not give a 404 but gives the Home controler About action.
Result
No more broken links but the site renders ugly urls.
For a site using lots of subdomains I use a nifty MVC extension from ITCloud called UrlRouteAttribute. It allows you to assign a route to every action as an attribute setting the path and name. I have extended this to allow fully qualified paths - so to include the domain/subdomain the controller should attach to. If this is something you'd be interested in I'll upload a copy somewhere.

Resources