Configure IIS7 to server static content through ASP.NET Runtime - asp.net

I searched high an low and still cannot find a definite answer.
How do I configure IIS 7.0 or a Web Application in IIS so that ASP.NET Runtime will handle all requests -- including ones to static files like *.js, *.gif, etc?
What I'm trying to do is as follows.
We have kind of SaaSy site, which we can "brand" for every customer. "Branding" means developing a custom master page and using a bunch of *.css and other images.
Quite naturally, I'm using VirtualPathProvider, which operates like this:
public override System.Web.Hosting.VirtualFile GetFile(string virtualPath)
{
if(PhysicalFileExists(virtualPath))
{
var virtualFile = base.GetFile(virtualPath);
return virtualFile;
}
if(VirtualFileExists(virtualPath))
{
var brandedVirtualPath = GetBrandedVirtualPath(virtualPath);
var absolutePath = HttpContext.Current.Server.MapPath(brandedVirtualPath);
Trace.WriteLine(string.Format("Serving '{0}' from '{1}'",
brandedVirtualPath, absolutePath), "BrandingAwareVirtualPathProvider");
var virtualFile = new VirtualFile(brandedVirtualPath, absolutePath);
return virtualFile;
}
return null;
}
The basic idea is as follows: we have a branding folder inside our webapp, which in turn contains folders for each "brand", with "brand" being equal to host name. That is, requests to http://foo.example.com/ should use static files from branding/foo_example_com, whereas http://bar.example.com/ should use content from branding/bar_example_com.
Now what I want IIS to do is to forward all requests to static files to StaticFileHandler, which would then use this whole "infrastructure" and serve correct files. However, try as I might, I cannot configure IIS to do this.

II7 already does that if the application pool's Managed Pipeline Mode is set to Integrated which is the default. In Integrated mode, ASP.NET handles all requests including those for static objects.
If you have to leave your application pool in Classic Mode then you need to use the same techniques you would use in IIS 6 to explicitly create handlers for the various static extensions.
Additional Information Based on Comments: I think your missing piece is creating an HttpHandler to handle the other extensions (.js, .css, etc.). Without this, then ASP.NET will use the default handling for these types of files. You would create a reference to you handler in your web.config. This article is an example of creating an HttpHandler for static files.

Kudos to everyone, but the problem was in totally different space.
VirtualPathProvider cannot be used in a pre-compiled web site. I'm furious.

Related

Routing for Single Page Application in ASP.NET Core

I have a Single Page Application written in JavaScript, and I use HTML5 history API to handle URLs on the client side. This means any URLs sent to the server should cause the server to render the same page.
In the ASP.NET MVC 5 I wrote this code to do this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
// ...
routes.Add(new Route("{*path}", new MyRouteHandler()));
}
}
public class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return WebPageHttpHandler.CreateFromVirtualPath("~/index.cshtml");
}
}
This worked really well. No matter what URL the server gets, it renders index.cshtml. Note that I am able to use a .cshtml file (as opposed to an .html file) which means I can have some C# code to dynamically change what .js scripts are included, append version numbers to .css files, and so on. What's more, I didn't have to implement controllers and views and so on just to render this .cshtml file.
Now we come to the question: How do you do this in ASP.NET Core? I have been reading the documentation, but I don't see how to render a .cshtml file without adding controller classes, view folders and other rigmarole.
Anyone know the equivalent code in ASP.NET Core?
Currently to run a CSHTML page "the normal way" in ASP.NET Core requires using ASP.NET Core MVC.
However, there is a planned feature that is being worked on that is somewhat similar to ASP.NET (non-Core) Web Pages, where you can have standalone CSHTML files. That feature is being tracked here: https://github.com/aspnet/Mvc/issues/494 (and as far as naming for the new feature, that is being tracked here: https://github.com/aspnet/Mvc/issues/5208).
There's also a sample of how to render an MVC view to a string (e.g. to generate an email, report, etc.), and that sample is available here: https://github.com/aspnet/Entropy/tree/dev/samples/Mvc.RenderViewToString
But to use this sample in the scenario you describe, you'd have to do some extra plumbing to wire it up as its own middleware (not a lot of plumbing; just a little!).
It's also worth noting that in your scenario you probably don't want all URLs going to this one view, because you still need the static files middleware running first to handle the CSS, JS, images, and other static content. Presumably you just want all other URLs to go to this dynamic view.

Keep URL extensions

I know that removing URL extensions is the new model for website programming. Unfortunately, my site is hosted on a hybrid server configuration. The call to my site goes into an Apache server that recognizes that my call is for a .aspx page, and passes the call along to an IIS server to complete the call. This complicates my website at this point because I am coding in Visual Studio 2015, and it models after the new rules of removing the extensions, and the call is never passed along to the IIS server.
I am not a big HTML guy, and I cannot find anything to place in web.config or my global.asax file for code to tell the system to overwrite the rule of removing the extension, and to keep my extensions. I have seen several posts here to remove the extensions, but nothing to keep them.
Basically, when I call www.mysite.com/Default.aspx, the current config removes the .aspx extension, and the call is for www.mysite.com/Default. I want to KEEP the .aspx extension on the call to the site so that it passes through the Apache server and to the IIS server. Any help would be greatly appreciated.
Have a look in your App_Start directory for a class called RouteConfig.cs and disable AutoRedirectMode using this line of code
settings.AutoRedirectMode = RedirectMode.Off;
This is what automatically removes extensions from your web pages.
Full example below...
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Off;
routes.EnableFriendlyUrls(settings);
}
}

Using MVC 3 to dynamically create Site.css on IIS 6. Works in dev, prod fails. Why?

I am using an MVC controller/action to dynamically create the site.css in my MVC 3 site. The way it works is I map a route to Site.css like so:
routes.MapRoute("CSS", "Content/Site.css", new { controller = "CSS", action = "Index" });
Then in my CSSController I have the following code:
public ActionResult Index() {
string path = Request.PhysicalPath.Replace("Site.css","SiteStyles.css");
byte[] data = null;
try {
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) {
data = ProcessCSS(fs);
}
} catch (Exception e) {
Logger.Write(new LogEntry(e.Message, "Error", 99, 1001, System.Diagnostics.TraceEventType.Error, "CSS Error", null));
throw e;
}
return File(data, "text/css","Site.css");
}
What's going on in the ProcessCSS method is it's going through SiteStyles.css and performing a find/replace on some constants I have defined, so that I can have a set of colors defined at the top as named constants, then anywhere I need that color in the site I just use the named constant. That way I can change the color once, and it propagates to all of the appropriate places in the stylesheet without me having to go touch 5-6 different places.
The above code works great debugging in my local environment and on the dev server, which is Windows Server 2003 running IIS6. However, when I deploy it to a production server that is also Windows Server 2003 running IIS6 the css is not being loaded and when I try to browse to Site.css (which should be getting mapped via my route) I get a 404 not found error. If I go to my dev server and get the output for Site.css and create a physical Site.css file on the production server, it works.
Any idea why this is failing on the production server?
I added some logging to my CSSControler to log where it is looking for SiteStyles.css and the log simply isn't being generated on the production server, which tells me the server is ignoring the mapped route entirely and not executing the code in the Controller.
Any suggestions on where to start are appreciated.
Sounds to me like you should double-check your IIS configuration on the production server to make sure that the necessary mappings are applied.
Are you using a wildcard (star) mapping in IIS on your development server, or did you define a mapping for the .css extension? You may need to verify that the production server has the same mapping.
Try to add write rights to the folder your css is located in to the user that your pool is run under.

Using Multiple Config files for a shared MVC Project

I have an MVC application that consists of a directory with all the necessary files to run except the css and master view. In IIS, I have several sites set up that all use the same directory, but then have virtual directories set up for each site's unique css and template. This works very well, although I am now wanting to have a separate config for each unique site (for basic settings like site id to query my db, etc). I created new virtual directories for each site and added a config.xml in each, added the following to my global.asax.cs file in the application start method:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
//read in all the application variables from the config file on startup
XmlDocument Doc = new XmlDocument();
Doc.Load(Server.MapPath("/Config/Config.xml"));
XmlNodeList VarLst = Doc.DocumentElement.SelectNodes("//AppVars/*");
foreach (XmlNode VarNod in VarLst)
{
RecursiveDescent(VarNod, "");
}
string[] AppVars = new string[Application.Count];
AppVars = Application.AllKeys;
//How do I now use Application.AllKeys from a controller?
}
Now, I have my keys, but can't seem to figure out how to use them in a controller. Normally, if I was using the app settings in the web config and wanted to set my siteId, I'd just use:
int siteId = Convert.ToInt16(WebConfigurationManager.AppSettings["SiteId"]);
but now I want to use
Application["SiteId"];
but it's not like a webforms code behind that inherits from the page class and I'm not getting the concept on how to get the Applications settings in an MVC controller.
As per this other question: Access "Application" object in ASP.Net MVC to store application wide variables
this.HttpContext.Application["SiteId"]

How do I get the complete virtual path of an ASP.NET application

How do I know the the complete virtual path that my application is currently hosted? For example:
http://www.mysite.com/myApp
or
http://www.mysite.com/myApp/mySubApp
I know the application path of HttpRequest but it only returns the folder name that my application is currently hosted, but how do I get the initial part?
The domain name part of the path is not really a property of the application itself, but depends on the requesting URL. You might be able to reach a single Web site from many different host names. To get the domain name associated with the current request, along with the virtual path of the current application, you could do:
Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath
Technically, an "application" is a virtual directory defined in IIS and Request.ApplicationPath returns exactly that. If you want to get the folder in which the current request is handled, you can do this:
VirtualPathUtility.GetDirectory(Request.Path)
ASP.NET has no idea how to distinguish your sub-application from a bigger application if it's not defined as a virtual directory in IIS. Without registering in IIS, it just sees the whole thing as a single app.
Request.Url
it contains several points that you might consider to use, see the image below:
The below code will solve the purpose, however you have to do a bit tuning for two types of scenarios:
Hosted as separate web application.
Hosted as Virtual application within a web application.
HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + HttpRuntime.AppDomainAppVirtualPath;
In .NET 4.5
VirtualPathUtility.ToAppRelative(path)
Try this (Haven't tried it)
public string GetVirtualPath(string physicalPath)
{
string rootpath = Server.MapPath("~/");
physicalPath = physicalPath.Replace(rootpath, "");
physicalPath = physicalPath.Replace("\\", "/");
return "~/" + physicalPath;
}
Link 1
Link 2
Url.Content("~") worked great for me and is nice and simple. I used it in the view like this:
<a href="#(Url.Content("~" + attachment))">
Here my attachment is a path like "/Content/Documents/Blah.PDF".
When my app is published to a IIS site that uses a virtual directory, Url.Content("~") resolves to just the virtual directory name like, "/app-test", for example.

Resources