MVC route to capture file name as a parameter - asp.net-mvc

I am attempting to produce a simple WebDAV server using MVC, and I have finally reached the stage where I need to serve up a requested file to the user.
I have a route set up that deals with traversing the directory structure "webdav/{*path}" which works fine, right up until the point where that path ends in a file name. At this point, it appears that IIS decides that it is a static file, and attempts to serve that file from the disk. As it isn't in the location specified in the URL, it returns a 404 error.
I don't have any freedom to change the url, I basically need it to be in the form, otherwise Windows Explorer can't work with it as a mapped drive:
GET /webdav/Test/Test2.txt
I've set the route to greedily match, as the directory structure can have as many levels. I've also set routes.RouteExistingFiles = true;
This is using IIS Express 8.0 on my development machine.
I've gone as far as setting up a blank MVC project just to test this, and this is the RegisterRoutes method:
routes.RouteExistingFiles = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "WebDAVGet",
url: "webdav/{*path}",
defaults: new { controller = "WebDAV", action = "Get", path = "" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
So, going to /webdav/Test/Test2 hits the breakpoint in my controller, but going to /webdav/Test/Test2.txt gives me a 404.
Any suggestions?

I needed to add
<modules runAllManagedModulesForAllRequests="true">
to the web config.
Ah, I've been struggling with this for a few days now, I knew posting here would shift the blockage!

Another option is to add this to the <system.webserver> node in web.config:
<modules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
</modules>
I can vouch that this works on IIS 7.5.
For the record, I found this solution here.

Related

MVC 5 - Serve a static txt file giving 404 error

I have to expose a txt file in my mvc 5 site's url in this path:www.mysite.com/home/somefile.txt
The .Net MVC is throwing 404 error.
I already added the handler on web.config
<add name="MyTxt"
path="/home/riot.txt"
verb="GET"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
What else i have to do for this work?
[Edit 1]
i created the route like this
routes.MapRoute(
name: "FileRoute",
url: "home/txt.txt",
defaults: new { controller = "Home", action = "GetFile" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
And i change the web.config
<modules runAllManagedModulesForAllRequests="true">
And here is my controller
public FileResult GetFile()
{
byte[] fileBytes = System.IO.File.ReadAllBytes(Path.Combine(HostingEnvironment.ApplicationPhysicalPath,"riot.txt"));
string fileName = "riot.txt";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
And it is throwing 404 error on url:http://localhost:10021/home/riot.txt
[EDIT 2] I put the wrong route pattern, after i put the right one home/riot.txt in this case. I worked. Thank you guys.
Seems you have a root which catches all /home requests - which is common in MVC templates. The simplest way is to change you file path:
/files/test.txt
But if you want to serve the file with /home path, you should change your HomeController's name, OR doing hard job:
Create an action in home named GetTxt() for example
Define a route just before your home-route with this pattern:
/home/txt.txt
Enable running all modules for all requests in web.config by setting runAllManagedModulesForAllRequests attribute in system.webServer/modules path:
But I wouldn't recommend that.
~/ resolves to the application root.
/ resolves to the site root.
Path="/Home/txt.txt"
use this ~/ Recommended in asp.net
Path="~/Home/txt.txt"

MVC Routing with relative path as parameter - How to escape the default behavior of searching for a file when link ends with extension?

I am looking for a way to make the MVC routing to hide upload path of a file, but considering it might be an html file that links to another html file I need to compensate for that.
routes.MapRoute(
name: "GetFile",
url: "File/{id}/{*path}",
defaults: new { controller = "Home", action = "GetFile", id = UrlParameter.Optional, path = UrlParameter.Optional }
);
So this leading to a url for example:
http://example.com/Files/5/Folder1/Folder2/Folder3/index.html
and if a link is clicked on that page to lead to: http://example.com/Files/5/Folder1/Folder2/Folder3/index2.html
But currently my problem is that if I add any file extension in the mix it dies because it is looking for the file itself, when I need it to just pass it as a text variable.
The problem you describe "if I add any file extension in the mix it dies because it is looking for the file itself" is not an MVC problem, but due to the default web server settings.
In IIS, there are settings to control how specific file extensions are handled. .html files are served directly through the web server by default and thus ignored by MVC. You can override this behavior by changing the handlers from their default settings.
1. Change HTTP Handlers in web.config to Handle .html Extension
<system.webServer>
<handlers>
<add name="HtmlFileHandler" path="*.html" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
<!-- other handlers... -->
</handlers>
</system.webServer>
2. Ensure runAllManagedModulesForAllRequests is false
Note this is the default so if this doesn't exist, don't add it.
<system.webServer>
<modules runAllManagedModulesForAllRequests="false" />
</system.webServer>
3. Add the Route
You have this (almost). But it probably doesn't make any sense to make id or path optional on the route. After all, what is it supposed to do if you don't supply the path? id can only be optional if it is not followed by path, so you would need to make another route to handle the no id case if it is indeed optional.
routes.MapRoute(
name: "GetFile",
url: "File/{id}/{*path}",
defaults: new { controller = "Home", action = "GetFile" }
);
4. Route Existing Files
To route the files in their original location, you need to apply the setting to routing, otherwise you will get an error if the URL is the same as a physical file on disk.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.RouteExistingFiles = true;
routes.MapRoute(
name: "GetFile",
url: "File/{id}/{*path}",
defaults: new { controller = "Home", action = "GetFile" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Reference: https://weblogs.asp.net/jongalloway/asp-net-mvc-routing-intercepting-file-requests-like-index-html-and-what-it-teaches-about-how-routing-works

Static File Routes in ASP.NET MVC

I'm working on an ASP.NET MVC app. In this app, I need to dynamically generate the sitemap when its requested. I know how to configure routes in general. However, I'm not sure if I can create a route for a specific file. Currently, I have the following in RouteConfig.cs:
routes.MapRoute(
name: "Sitemap",
url: "resources/sitemap.xml",
defaults: new { controller = "Site", action = "Sitemap" }
);
In my SiteController, I have the following:
public ActionResult Sitemap()
{
// I will build my sitemap.xml file here and return it.
}
When I enter /resources/sitmap.xml into the browser's address bar, I noticed that my Sitemap() action never gets tripped. Is it even possible in ASP.NET MVC to setup a route for a specific file? If so, how?
Thanks,
So you got to do in some steps -
Step 1 - Map the xml extension to take care of by .Net for routing
Add following section in Web.config under <system.webServer> -
<handlers>
<add name="HtmlFileHandler" path="*.xml" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
Step 2 - Define your routes and override requests that match an existing file.
routes.RouteExistingFiles = true;
routes.MapRoute(
name: "Sitemap",
url: "{site}.xml",
defaults: new { controller = "Site", action = "Sitemap", site = UrlParameter.Optional }
);
then step 3 - Try to access /SiteMap.xml, you will get Controller action hit.

ASP.NET MVC 4 Catch-all only firing on local, not on remote requests

In an ASP.NET MVC 4 application, we have set up a catch-all route as follows:
routes.MapRoute(
name: "UnKnown",
url: "{*url}",
defaults: new { controller = "CatchAll", action = "UnknownUrl" });
The UnknownUrl method in the CatchAllController correctly loads its view in our development environment.
However, the production IIS 7.5 shows its standard 404 page if a non-existing remote request arrives. A local request, sent using RDP on the server itself, works fine.
The web.config is set tp
<customErrors mode="Off"/>
What other difference is there between a local call and a remote call? How can we make the MVC HttpHandler catch those requests?
A hint might be that we were also unable to make the IIS show any detailed status 500 error messages when called remotely.
I have had problems with IIS showing default errors instead of .NET errors, which I've fixed with the following in system.webServer in the web.config:
<httpErrors existingResponse="PassThrough"/>
I think this would happen if in your UnknownUrl action you are setting Response.StatusCode = 404;. By default IIS sees you are returning an error code so shows a default error message, which you can override with that config setting.
I'm not sure this would be different on local v remote but could be worth a try.
Can you try setting the host header - I believe this is what causes the difference between local and production:
new { controller = "CatchAll", action = "UnknownUrl", host = HttpContext.Current.Request.Url.Host}
Register this route from Application_BeginRequest of Global.asax. Also, ensure that this is done only once - perhaps by a check similar to:
if (routes["UnKnown"] == null)
{
routes.MapRoute(
name: "UnKnown",
url: "{*url}",
defaults: new { controller = "CatchAll", action = "UnknownUrl", host = HttpContext.Current.Request.Url.Host}
);
}

ASP.NET MVC 3: RouteExistingFiles = true seems to have no effect

I'm trying to understand how RouteExistingFiles works.
So I've created a new MVC 3 internet project (MVC 4 behaves the same way) and put a HTMLPage.html file to the Content folder of my project.
Now I went to the Global.Asax file and edited the RegisterRoutes function so it looks like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.RouteExistingFiles = true; //Look for routes before looking if a static file exists
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {controller = "Home", action = "Index", id = UrlParameter.Optional} // Parameter defaults
);
}
Now it should give me an error when I'm requesting a localhost:XXXX/Content/HTMLPage.html since there's no "Content" controller and the request definitely hits the default pattern. But instead I'm seeing my HTMLPage.
What am I doing wrong here?
Update:
I think I'll have to give up.
Even if I'm adding a route like this one:
routes.MapRoute("", "Content/{*anything}", new {controller = "Home", action = "Index"});
it still shows me the content of the HTMLPage.
When I request a url like ~/Content/HTMLPage I'm getting the Index page as expected, but when I add a file extenstion like .html or .txt the content is shown (or a 404 error if the file does not exist).
If anyone can check this in VS2012 please let me know what result you're getting.
Thank you.
To enabling routing for static files you must perform following steps.
In RouteConfig.cs enable routing for existing files
routes.RouteExistingFiles = true;
Add a route for your path ( Make sure specialized path are above generalized paths)
routes.MapRoute(
name: "staticFileRoute",
url: "Public/{file}/",
defaults: new { controller = "Home", action = "SomeAction" }
);
Next configure your application, so that request for static files are handeled by "TransferRequestHandler".In Webconfig under system.webServer>handlers add following entry.
<add name="MyCustomUrlHandler2" path="Public/*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
The value of 'path' can be more generic or specific depending on your requirement. But i prefer it to be always very specific as per one's need. Keeping it very generic will block serving of other site specific resources such as .js or css files. For example if above is set as path="*", then request for even the css (inside the content folder) which is responsible for how your page would look will also end up in your Controller's action. Something that you will not like.
Visual Studio 2012 uses IIS Express. You need to tell IIS not to intercept requests for disk files before they are passed to the MVC routing system. You need set preCondition attribute to the empty string in config file:
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule"
preCondition="" />
In Win7/8 you can find config file on this path: %userprofile%\Documents\IISExpress\config\applicationhost.config
The RouteExistingFiles doesn't keep files from being viewed if there is no route for them, it just checks the routes before checking if the file exists. If there is no matching route, it will continue to check if there is a matching file.

Resources