Why CSS and JS files bypass Asp.Net MVC routes? - c#

I received a prototype application built with Asp.Net MVC4. It is currently replacing the default controller factory with a custom one using NInject, ServiceLocator and all.
The problem is that by replacing the default controller factory, the requests to JS files are being treated as if it was a legit request for a controller and an action.
So, looking at the default template create by Visual Studio, route configuration looks like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
After looking that, I'm asking myself: How come a request to "/Scripts/jquery.js" does not get interpreted by Asp.Net MVC? I mean, why doesn't it think "Script" is a controller and "jquery.js" is an action?
Because the project works if I disable the controller factory override, I can only assume that the default factory is the responsible for that kind of check. And that would mean that a "/Scripts/jquery.js" are indeed passed to the controller factory which is something I didn't really know.
Could anyone shed some light on that?
What kind of treatment should one do when overriding the controller factory to avoid such problems?

It's not because of how MVC handles the request to jquery.js it's because of the way IIS handles the request to jquery.js. IIS assumes that resources such as .js, .jpg, etc, are all static resources, and thus doesn't need to pass them through the ASP.NET engine. In order to to prevent this from occurring you can add a line to the web.config for a path that you want IIS to leave alone.
<system.webserver>
<handlers>
<add name="scripts" path="/Scripts/*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
</system.webserver>
Adding something like that should allow your JS files to be served via ASP.NET and not directly through IIS.

After a little bit more of research I found the following quote from Steven Sanderson's book:
However, the routing system still does check the file system to see if an incoming URL happens to match a file or disk, and if so, routing ignores the request (bypassing any route entries that the URL might also match) so that the file will be served directly. This is very convenient for static files, such as images, CSS, and JavaScript files. You can keep them in your project (e.g., in your /Content or /Script folders), and then reference and serve them directly, just as if you were not using routing at all. Since the file genuinely exists on disk, that takes priority over your routing configuration.
If, instead, you want your routing configuration to take priority over files on disk, you can set the RouteCollection’s RouteExistingFiles property to true. (It’s false by default.)
That was something very interesting to learn and led me to the actual problem. A much simpler one. As it happened, the pertinent scripts were not present on the folder. At least not the ones with the exact same version requested on the view. That was the responsible for Asp.Net MVC thinking it was a controller/action request.
Reference: http://forums.asp.net/t/1536510.aspx/1

check that use "~/" before your Links or Scripts like :
src="~/assets/js/main.js"

Related

MVC core routing not functioning as expected

So it's my first time setting up an netcore MVC based application. I've used MVC 4 in the past on plain old asp.net.
So i'm having issues with my routing. My application is an single page application (spa) that is accessible from the home controller on the index action. I can access this controller method fine, and my defaults are set so that this is navigated to at route: /.
I also have a second controller for authentication called AccountController. This controller's methods take and return JSON, rather then views. I can also access the methods on this controller from my application.
The issue i'm having lies in my next controller, which is the start of my API.
As such, i've put it in a folder called api inside my controllers folder. However, no matter what i try, i cannot seem to get the methods on the controller accessible. I have also tried moving it out of the api folder and just having in the route of the controllers folder.
The routing deffinition
app.UseMvc(routes =>
{
routes.MapRoute(
name: "api",
template: "api/{controller=Core}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I've tried adding and removing the api definition, removing the api part, and adding a template for actions aswel, all to no effect.
The troublesome controller
public class CoreController : Controller
{
[HttpGet]
public JsonResult Get()
{
return Json("Dev");
}
}
I've tried adding [Route(~routing here~)] annotations to this controller and its methods with no success either.
Folder structure
I should also mention that i've tried plenty of URL's to access this controller on:
/api/Core/
/Core/
/api/Core/Get
I've been wracking my brain for the best part of a day trying to get this sorted and i know i'm missing something obvious, i just can't for the life of me work out what it is.
Edit:
I've added a cut-down sample of my project to github at: https://github.com/lexwebb/aspnet-test if anyone would like a complete example
Edit 2
It appears that my example works, i'm going to add things in to see what breaks it
AFAIK, default route requires the {action} using as well.
Instead of "api" default routing, you may to use the following configuration for such type of controllers (RESTFul controller):
[Route("api/[controller]")]
public class CoreController : Controller
{
[HttpGet]
public JsonResult Get()
{
return Json("Dev");
}
}
I found this Routing is ASP.NET Core article useful in the past.
So as it turns out, i had made a mistake in a totally unrelated place. I had renamed my project half way through the beginning stage of development, after i had build scripts in place. This led to the the wrong dll being referenced on the server when the code was ran, a version that had all of my routing EXCEPT the new one, of course.

ASP.NET MVC controller handling file downloads

I've built a controller in MVC 5 to handle file downloads protected by login. The controller works fine without the pdf extension, ie paths like /media/1001/secretpdffile will download a file at App_Data/media/1001/secretpdffile.pdf only if the user is logged in.
I would like to have the pdf extension on the paths but as soon as I add it (of course also dropping the pdf I'm adding in the controller) that I get a 404 error.
This is my route
routes.MapRoute(
name: "Media",
url: "media/{mediaid}/{filename}",
defaults: new { controller = "Media", action = "Get" }
);
I've seen a lot of places that try to solve this with <httpRuntime relaxedUrlToFileSystemMapping="true" /> or with setting a path with System.Web.Handlers.TransferRequestHandler in web.config but I haven't had any luck with that.
This is a normal behavior from IIS, called request filtering.
Basically it will not send the request to ASP.MVC if there is a dot because it will think that you want a file, so it will look for the file on the server and return a 404 error because it can't find it.
You can find some solution here : https://stackoverflow.com/a/12151501/1681023
Or you can edit your configuration on IIS with disabling the feature like the first link show.
Or you can escape your dot, by replacing with %2E, it might be the easiest way.
On a side note, be careful with disabling the request filtering feature, obviously if you disable it for https://stackoverflow.com/media/* you won't be able to reach any resources in a folder named media from a http request, So if you want some image, css or js on your website, don't disable it for all your app or server.
I changed the route url to "media/{mediaid}/{filename}.{format}" and updated the controller to expect the format parameter.
I also had to add this to the web.config
<add name="ManagedFileWithExtension" path="/media/*/*.*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />

MVC 3 tries to launch URL to View instead of controller action

Sometimes when I launch my MVC 3 project it attempts to load the fully qualified URL for the view being rendered instead of the action within the controller (Which gives me a 404 error). Other times it works fine and actually hits the controller action like it's supposed to, but it's about 50/50.
The URL it hits sometimes is: http://localhost:xxxx/Views/Account/LogOn.cshtml
Here is the default route setup in the Global.asax file:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Account", action = "LogOn", id = UrlParameter.Optional }
);
I also tried removing the /{id} parameter from the route as I don't feel it's needed for the logon screen.
Any ideas? Currently the project is setup pretty simply with the default action method LogOn in the AccountController etc. The only thing I did was change the controller and action in the global.asax file.
Try this :go to Project Properties > Web > Start Action
And check the Specific Page option - leaving the text box blank.
You are probably using Visual Studio and you probably are actively editing a .cshtml page when you hit debug.
Try launching the debugger when you are either looking at a code file or a file from a project that isn't in the startup project (ie, your EF/model project) and see if that launches the debugger to the correct URL.
There might be a setting in the project properties that specifies the startup URL. I'll look for it and edit this post if I find it.
I'm guessing you using cassini (builtin dev web server in VS.Net)? If so I get this all the time and seams to be a bug in VS.Net. Switch to IIS 7.5 and you don't get it any more

How to use URL routing with multiple environments?

I have three different environments that I need to be able to use url routing with:
Debug
Live
Demo
The home pages for these three are:
http://localhost:48060/Login.aspx
http://192.168.0.145/Live/Login.aspx
http://www.website.com/Demo/Login.aspx
Both Live and Demo sit in the same Default Web Site as web applications (live is exposed only internally, while demo is exposed externally).
I want to map these to
http://localhost:48060/login
http://192.168.0.145/Live/login
http://www.website.com/Demo/login
Without triplicating every route mapping, what is the recommended approach?
Thanks!
Example of how I add the route for Debug env:
routes.MapPageRoute("Login", "login", "~/Views/Login.aspx");
More info:
When I tried adding
routes.MapPageRoute("Login", "login", "~/Live/Views/Login.aspx");
routes.MapPageRoute("Login", "login", "~/Demo/Views/Login.aspx");
the routes didn't work. I received a 404 error when trying visit http://192.168.0.145/Live/login Not sure what the problem is.
I'm using IIS 7.1 for published versions and whatever Win XP pro uses for debug.
Youre "environments" seem to be sub-directories of the root of the application. Because the first of your three URLs does not contain a second value (e.g. "http://localhost:48060/debug/login") it's not going to be easy to define one route for all three.
If these secondary environments are defined as their own applications then you should be able to use the same route in each, but we would need more details to help you further.
Please describe your situation a little better and I will update my answer with more information.
Turned out to be a configuration issue that m$ forgot to mention. Got it working by modifying my web.config to use runAllManagedModulesForAllRequests
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">

Rewriting URLs in ASP.NET?

I am using ASP.NET C#.
How do I implement URL re-writing procedure that is similar to StackOverflow.com?
http://stackoverflow.com/questions/358630/how-to-search-date-in-sql
Also, what is the meaning of values such as "358630" in the URL? Is this the question ID (the basis for which they use to fetch the data from the table)? Whatever it is, in my application I am identifying records using an "ID" field. This field is an identity column in an SQL table. Right now, my URLs are like the following:
http://myweb.com/showdetails.aspx?id=9872
But I'd like them to appear like:
http://myweb.com/showdetails/9872/my_question_title
Or:
http://myweb.com/9872/my_question_title
Or whatever the best way, which will taste good to search bots.
My application is hosted on Go Daddy's shared hosting service, and I feel that no customized ASP.NET "HTTP module" or no customized DLL for URL re-writing is working on their server. I tried many samples but no luck yet!
I found that Stack Overflow is hosted on Go Daddy (shared hosting?). Maybe Stack Overflow's method will work for me.
SO is using ASP.NET MVC. You really need to read in details how MVC URL rewriting works, but the gist of it is that the 'questions' part in the URL is the name of the Controller class (which roughly corresponds to the 'showdetails' in your URL) and the number is a ID parameter for the default action on that Controller (same as the parameter 'id' in your URL).
Since MVC isn't an option you can try redirecting the 404s. This will work in ASP.NET 1.1 and above: Redirect 404s and 405s to your own handler using either IIS config or web.config, parse out the request in the handler and redirect to the appropriate resource.
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="error.html">
<error statusCode="404" redirect="newHandler.aspx"/>
</customErrors>
</system.web>
</configuration>
Before the advent of System.Web.Routing, the common practice was to use UrlRewriter.NET. Worked well enough, but could bite you when configuring IIS. I'm not sure if there are any simple ways of using the new Routing classes in ASP.NET (i.e., drop it in and go vs. refactoring code).
please explain the meaning of values
such as "358630" in the URL
That is (presumably) the ID for the question in the database. In the MVC model
myurl.com/questions/358630
is analogous to
myurl.com/questions.aspx?id=358630
The question title on the end of the URL is actually being ignored by the app. It's generally "tacked on" for search engine optimization and human readability purposes. In fact, you can change the title of this question in the URL and notice the page still loads just fine.
The new System.Web.Routing dll is part of ASP.NET 3.5 SP1, and is bin deployable on ASP.NET 3.5, so you could use the features of that on a classic ASP.NET WebForms site.
You'll probably want to take note of Phil Haack's comments in his post on using MVC on IIS 6 as you'll probably need to include the .aspx extension in your routed urls
http://www.mysite.com/controler.aspx/action/id
You might also want to check out Questions Tagged SEO.
The ignored question name at the end of the url is often called a "Slug", and is used for SEO purposes to include the page title in the url.

Categories