ASP.NET MVC controller handling file downloads - c#

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" />

Related

Route ashx path to ApiController

I have a legacy application where an ashx path used to go to an HttpHandler but I would like it to go to an ApiController without changing the path.
So my.application.com/auth/gettoken.ashx used to go to an "old-school" ashx.cs file, but now I want it to go to a regular WebAPI controller.
I've tried removing the handler and adding an ApiController with the correct route. This works locally, but not in production. I assume because my local IIS Express is configured differently than the production IIS.
Can I achieve this? I would be okay with disabling ashx handlers altogether. I've tried this in my web.config, but it didn't work:
<remove name="SimpleHandler"/>
I've looked into a redirect, but I'm doing a POST to this URL, so a redirect won't seem to work either.
So, can I, in any way, have a call to a path with an .ashx extension be routed to a regulare WebAPI ApiController?
I've currently reintroduced the ashx file, as I didn't find any other solution. If someone ever does, I'd happily mark that as the accepted answer (and change my code).

URI in DELETE request's URI with MVC 5

We have a version control service which should be accessible through our REST API. One of those operations allows to delete a directory in SVN. Ideally, I'd like to send a DELETE request with the URI of the target to delete, something like this: http://service:4711/directory/http%3A%2F%2Fsome%2Fdirectory
What happens isn't new and there are plenty of answers out there. Unfortunately, they do not work for me. Depending on what I try, I get a 404 or a 403 (due to the malicious colon).
Let me show you some code and what I've tried without success so far:
// The action in my controller
[HttpDelete]
[Route("directory/{uri}/")]
public void DeleteDirectory(string uri)
{
var x = HttpUtility.UrlDecode(uri);
}
I am using MVC version 5.2.3.0.
I've tried:
[System.Web.Mvc.ValidateInput(false)] on the action and/or the class.
Setting runAllManagedModulesForAllRequests="true" in the web.config.
Setting requestPathInvalidCharacters="" in the web.config.
Setting requestValidationMode="true" in the web.config.
Right now, I see four possible solutions:
I've done something wrong with the previous approaches.
I have to create a custom RequestValidator.
I have to double encode the URI in the request.
Send a POST request instead of DELETE.
One may say, put it in the body of the DELETE request. But this option is highly controversial, so I'd like to ignore this one from the very beginning.
So what have I done wrong and what do you suggest to do?
Best regards,
Carsten
Colons in URI's in MVC are not allowed to be used until after the querystring '?' character in an URL, even when it is encoded as %3A.
Therefore, unless the SVN is http/s independent you could drop the initial http: from the parameter passed in an append it in the code.

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

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"

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

URL with no query parameters - How to distinguish

Env: .NET 1.1
I got into this situation. Where I need to give a URL that someone could redirect them to our page. When they redirect they also need to tell us, what message I need to display on the page. Initially I thought of something like this.
http://example.com/a.aspx?reason=100
http://example.com/a.aspx?reason=101
...
http://example.com/a.aspx?reason=115
So when we get this url based on 'reason' we can display different message.
But the problem turns out to be that they can not send any query parameters at all. They want 15 difference URL's since they can't send query params. It doesn't make any sense to me to created 15 pages just to display a message.
Any smart ideas,that have one URL and pass the 'reason' thru some means?
EDIT: Options I'm thinking based on Answers
Try HttpRequest.PathInfo
or Second option I was thinking was to have a httphanlder read
read the path like this - HttpContext.Request.Path
based on path act. Ofcourse I will have some 15 entries like this in web.config.
<add verb="*" path="reason1.ashx" type="WebApplication1.Class1, WebApplication1" />
<add verb="*" path="reason2.ashx" type="WebApplication1.Class1, WebApplication1" />
Does that look clean?
Thoughts:
Path Info: http://msdn.microsoft.com/en-us/library/system.web.httprequest.pathinfo.aspx
urls would be http://example.com/a.aspx/reason100, http://example.com/a.aspx/reason101, etc
URL Rewriting : http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx
urls would be http://example.com/a/reason/100.aspx, http://example.com/a/reason/100.aspx, etc.
edit: both these approaches involve only one aspx page, but multiple urls pointing to it.
Assuming IIS (I run this on IIS 6 but I expect it would run on 5 as well) you could install IIRF. You could then configure different "friendly" urls a la Apache's mod-rewrite and send them as query params to a single as*x page.
Can they send POST variables?
Too bad you are at 1.1 because the later versions support routing which allows for RESTful URLs.
Another option would to be write a custom HttpModule and intercept the incoming requests.

Categories