Call to Url.RouteUrl not working on deployed website - asp.net-mvc-3

In an ASP MVC3 project a call to:-
<h2>#programa.NombrePrograma(#idioma)</h2>
corresponding to a registered route in the Global.asax.cs file of:-
routes.MapRoute(
"_ScientificPrograms", // Route name
"{IdiomaSeleccion}/research/scientific-programmes/{idProgramaSeleccionado}/{nombrePrograma}/{idGrupoSeleccionado}/{nombreGrupo}",
new { controller = "Research", action = "ScientificProgrammes", IdiomaSeleccion = UrlParameter.Optional, idProgramaSeleccionado = UrlParameter.Optional, nombreGrupo = UrlParameter.Optional, idGrupoSeleccionado = UrlParameter.Optional, nombrePrograma = UrlParameter.Optional }
);
works fine running on my local machine producing urls like:-
http://localhost/es/research/scientific-programmes/1/molecular-oncology
When the website is deployed, however, the same call only produces
http://deployedsite/es/research/
although entering
http://deployedsite/es/research/scientific-programmes/1/molecular-oncology
does correctly resolve the page.
The same behaviour is observed both on my own test hosting and on my clients QA server. Other routing calls work fine but it appears that this one is some what special.

You likely have solved this or moved on, but I will put this information here in case someone else comes across your question because they are suffering from the same MVC3 bug that causes this issue.
It turns out that consecutive UrlParameter.Optional parameters causes a small bug in MVC3, as per this blog entry from Phil Haack.
The bug manifests when you have a route with two consecutive optional URL parameters and you attempt to use the route to generate an URL. The incoming request matching behavior is unchanged and continues to work fine.
The workaround is simple. To fix this issue, change the existing route to not have any optional parameters by removing the default values for month and day. This route now handles the first URL where month and day was specified.
We then add a new route for the other two cases, but this route only has one optional month parameter.
In my case, I had a route like this:
routes.MapRoute(
"HouseholdComments",
"Comments/Household/{id}/{saQid}/{clId}",
new { controller = "Comments", action = "Household", id = UrlParameter.Optional, saQid = UrlParameter.Optional, clId = UrlParameter.Optional }
);
And I was trying to use Url.RouteUrl like this:
var questionHref = '#Url.RouteUrl("HouseholdComments", new {id=#Model.SafetyForm.SAS_SEQ_NO})'
thinking that it should just call the route with only 1 parameter. But it did not, it returned null. As in Phil's article above, I had to add a separate route for this case after my HouseholdComments route:
routes.MapRoute(
"HouseholdCommentsBase",
"Comments/Household/{id}",
new {controller = "Comments", action="Household", id=UrlParameter.Optional}
);
I then was able to use that route in Url.RouteUrl to properly get me the link I needed.
I'm guessing that you should just need to add another route to handle the case where you are only using only those 3 parameters in Url.RouteUrl.

It turns out that the application of the latest .NET service packs to the server fixed the problem with no code changes. The It-Works-On-My-Machine behaviour turned out to be the smoking gun.

Related

.net core mvc routing and controllers

So I am brand new to .net. I am learning .net core right now. I am trying to figure out routing. I can not seem to get the routing to look in any folder except Home and Shared. I have looked all over the internet and tried many things. There seems to be something I am missing. Here is what I got
app.UseMvc(routes =>
{
routes.MapRoute(
name: "test",
template: "Register/test",
defaults: new { controller = "Register", action = "test"}
);
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I have a Register folder with a test.cshtml file in just to try t figure this routing out. And this is is in my HomeController.cs file
public IActionResult test()
{
return View();
}
on my _Layout page I have this link
<li><a asp-area="" asp-controller="Register" asp-action="test">Test</a></li>
It works fine when I put it in the home folder, but I want to keep things separate. I know there is something I am missing. I've poured through all kinds on articles online including Stack Overflow, and I just don't understand what I am missing. From what I read its suppose to be like the Parent folder/File/ then and id that may be attached to that like a user name I have tried other formats for the routing with no luck, this was just my most recent attempt. I just can't help but think I need some bit of code somewhere else.
From your question it looks like you have the following code in your HomeController.
public IActionResult test()
{
return View();
}
That actually belongs in your RegisterController because the route template you defined is an explicit capture with defaults to the "Register" controller and the "test" action.
The view called "test.cshtml" - which should be named as such because of the default convention - should reside in your \Views\Register folder, next door to \Views\Home.
There are a couple of reasons why this may have worked in some fashion. First, the view is discoverable for any controller if it's in shared. Without knowing more about the requests you tried, it's difficult to determine if routing kicked in on the first route or second, but if that method was truly on HomeController requests to /home/test would have worked.
It looks to me like you're exploring routing. That is great - I 100% encourage the experimentation - so long as you know that routing isn't necessarily the lowest hanging fruit to learn. It's also something that you shouldn't have to touch 93.7% of the time. For example, the route you have defined about wouldn't be required for the controller and action you're adding with RegisterController and test.
Cheers.

Routing Conventions in Can.js

So I’m looking to make some routes within my super cool can.js application. Aiming for something like this…
#!claims ClaimsController - lists claims
#!claims/:id ClaimController - views a single claim
#!claims/new ClaimController - creates a new claim
#!claims/:id/pdf - do nothing, the ClaimController will handle it
#!admin AdminController - loads my Administrative panel with menu
#!admin/users - do nothing, the AdminController will handle it
#!admin/settings - do nothing, the AdminController will handle it
So how might we do this?
“claims route”: function() { load('ClaimsController'); },
“claims/:id route”: function() { load('ClaimController'); },
“admin”: function() { load(‘AdminController’); },
Cool beans, we’re off. So what if someone sends a link to someone like...
http://myapp#!claims/1/pdf
Nothing happens! Ok, well let’s add the route.
“claims/:id/pdf route”: function() { load('ClaimController'); },
Great. Now that link works. Here, the router’s job is only to load the controller. The controller will recognize that the pdf action is wanted, and show the correct view.
So pretend I’ve loaded up a claim claims/:id and I edit one or two things. Then I click the Print Preview button to view the PDF and change my route to claims/:id/pdf.
What should happen… the Claim Controller is watching the route and shows the pdf view.
What actually happens… the router sees the change, matches the claims/:id/pdf route we added, and reloads the Claim Controller, displaying a fresh version of the claim pulled from the server/cache, losing my changes.
To try and define the problem, I need the router to identify when the route changes, what controller the route belongs to, and if the controller is already loaded, ignore it. But this is hard!
claims //
claims/:id // different controllers!
claims/:id //
claims/:id/pdf // same controller!
We could just bind on the "controller" change. So defining routes like can.route(':controller') and binding on :controller.
{can.route} controller
// or
can.route.bind('controller', function() {...})
But clicking on a claim (changing from ClaimsController to ClaimController) won't trigger, as the first token claim is the same in both cases.
Is there a convention I can lean on? Should I be specifying every single route in the app and checking if the controller is loaded? Are my preferred route urls just not working?
The following is how I setup routing in complex CanJS applications. You can see an example of this here.
First, do not use can.Control routes. It's an anti-pattern and will be removed in 3.0 for something like the ideas in this issue.
Instead you setup a routing app module that imports and sets up modules by convention similar to this which is used here.
I will explain how to setup a routing app module in a moment. But first, it's important to understand how can.route is different from how you are probably used to thinking of routing. Its difference makes it difficult to understand at first, but once you get it; you'll hopefully see how powerful and perfect it is for client-side routing.
Instead of thinking of urls, think of can.route's data. What is in can.route.attr(). For example, your URLs seem to have data like:
page - the primary area someone is dealing with
subpage - an optional secondary area within the page
id - the id of a type
For example, admin/users might want can.route.attr() to return:
{page: "admin", subpage: "users"}
And, claims/5 might translate into:
{page: "claims", id: "5"}
When I start building an application, I only use urls that look like #!page=admin&subpage=users and ignore the pretty routing until later. I build an application around state first and foremost.
Once I have the mental picture of the can.route.attr() data that encapsulates my application's state, I build a routing app module that listens to changes in can.route and sets up the right controls or components. Yours might look like:
can.route.bind("change", throttle(function(){
if( can.route.attr("page") == "admin" ) {
load("AdminController")
} else if(can.route.attr("page") === "claims" && can.route.attr("id") {
load("ClaimController")
} else if ( ... ) {
...
} else {
// by convention, load a controller for whatever page is
load(can.capitalize(can.route.attr("page")+"Controller")
}
}) );
Finally, after setting all of that up, I make my pretty routes map to my expected can.route.attr() values:
can.route(":page"); // for #!claims, #!admin
can.route("claims/new", {page: "claims", subpage: "new"});
can.route("claims/:id", {page: "claims"});
can.route("admin/:subpage",{page: "admin"});
By doing it this way, you keep your routes independent of rest of the application. Everything simply listens to changes in can.route's attributes. All your routing rules are maintained in one place.

Routing document-relative static urls in MVC3

I'm integrating a JavaScript library into an ASP.NET MVC3 web app. The library assumes it will be installed next to the page that references it, and so it uses document-relative URLs to find its components.
For example, the default directory layout looks like
container-page.html
jslibrary/
library.js
images/
icon.png
extensions/
extension.js
extension-icon.png
However, I want to reference the library from the view in /Home/edit. I install the library in the default Scripts\jslibrary\ When I reference the library in the view in Views\Home\edit.cshtml, the library's document-relative links like
images/icon.png
end up as requests to
http://localhost/Home/images/icon.png
which results in a File Not Found (404) error. How do I construct a route to look for
{anyControllerName}/images/{anyRemainingPathInfo}
and serve up
http://localhost/Scripts/jslibrary/images/{anyRemainingPathInfo}
?
(full disclosure: I'm still on IIS 6 in Production, and not much chance of going to IIS7 any time soon, so if this is better done at the IIS level, please account for IIS6. Thanks!)
You could create a controller for handling you redirect logic - for example an "Images"controller. Register a global route in your Global.asax file, using the pattern (more on this type of pattern here:
routes.MapRoute(
"Images", // Route name
"{xyz}/{controller}/{path}", // URL with parameters
new {controller = "Images", action = "Index", path= UrlParameter.Optional} // Parameter defaults);
In your controller:
public ActionResult Index(string path)
{
//format path, parse request segments, or do other work needed to Id file to return...
return base.File(path, "image/jpeg"); //once you have the path pointing to the right place...
}
Not sure if this solution will work for you, wish I could come up with something more elegant. Best of Luck!
Short of rewriting the library and having it check for the appropriate directory the only solution I can think of is to include the views, library and supporting files in a directory structure that the library can access. This of course would break MVC's convention over configuration way of finding views, so you would have to write a custom override of the way Razor looks for views, which is not too complex to do, but you might be making life more difficult for yourself down the road depending on your application. Your call which is the lesser of the two evils :) (I'd go for fixing the library)
Make a help function
#functions{
public string AbsoluteUrl(string relativeContentPath)
{
Uri contextUri = HttpContext.Current.Request.Url;
var baseUri = string.Format("{0}://{1}{2}", contextUri.Scheme,
contextUri.Host, contextUri.Port == 80 ? string.Empty : ":" + contextUri.Port);
return string.Format("{0}{1}", baseUri, VirtualPathUtility.ToAbsolute(relativeContentPath));
}
}
Calling
#AbsoluteUrl("~/Images/myImage.jpg") <!-- gives the full path like: http://localhost:54334/Images/myImage.jpg -->
This example are from
https://dejanvasic.wordpress.com/2013/03/26/generating-full-content-url-in-mvc/

'The resource cannot be found' - error for 1 specific controller asp.net mvc

I've been pulling my hair out on this one.
I have a controller: ChatController of which any route I try gives me a 'The resource cannot be found' error.
The route:
routes.MapRoute(
"chatPage",
"{lang}/chat/{action}",
new { lang = "th", controller = "Chat", action = "Index" }
);
The url: /th/chat
All my other routes (to other controllers) work fine.
I use routedebug.dll to see if my routes are set up correcty and the route is correct.
Even if I delete the whole ChatController, I still get the same error. That means it does find the route but doesn't even get to the controller (usually this will give an controller not found error or something similar)
I get this on development server and IIS.
Anyone?
* Update *
I got it working by changing the routevalue from {lang}/Chat to {lang}/chat and changed my controllername accordingly (ChatController => chatController), this works... for now. Still want to know what causes this.
make sure that previous routes don't match this case. comment all others routes and then try. in my computer it's works for me :)

T4MVC causing incorrect URL's

Using T4MVC on my new MVC Razor Project and I will have an action link like so
#Html.ActionLink(ViewRes.SharedStrings.HomeLink, MVC.Home.Views.Index, null, new { rel = "dropmenu7" })
so i would expect a url like
http://localhost:52122/Home/Index
but what i am getting is
http://localhost:52122/Home/~/Views/Home/Index.cshtml
looking into the t4mvc template file, i see where the "~/Views/Home/Index.cshtml" is coming from but I dont wanna touch it because it was made that way and i would guess i shouldnt have to change anything there.
Asking a friend, he says that i should use RouteLink instead of ActionLink because i am sometimes going to locations outside of the controller. However when i do that, i get: "A route named '~/Views/Home/Index.cshtml' could not be found in the route collection." when i try to run the app.
I guess i should also note that the links i am using are in the _Layout.cshtml
What am i doing wrong?
You need to change 'MVC.Home.Views.Index' to 'MVC.Home.Index()':
#Html.ActionLink(ViewRes.SharedStrings.HomeLink, MVC.Home.Index(), null, new { rel = "dropmenu7" })

Resources