Project subdirectory as root for static files - asp.net-mvc

New to ASP.NET MVC, I am creating a web application using the Visual Studio 2013 wizard. It creates several folders from where static files are served: Content, Scripts, etc.
Frameworks in other languages (e.g. TurboGears) have an explicit directory only for static content, removing the risk of serving the source code of a page instead of processing it which is a typical configuration mistake of PHP sites.
ASP.NET however is happy to deliver anything in the application's root directory, e.g. http://localhost:1740/Project_Readme.html as long as it has the right extension. Only the Views folder is protected with a Web.config.
How do I configure the application to use another directory than the project's root directory for static files. E.g. if the file favicon.ico is put into the subdirectory Content, it should be accessible as http://localhost:1740/favicon.ico, but nothing outside of the Content directory unless returned by a controller.
Nothing should ever be executed in this directory, that is, if *.cshtml files are put into this directory, the files' contents (the source code) should be delivered as text/plain.
Final application will run using mod_mono on Linux.

Update:
Ben,
The proposed solution works only with Owin. To get it working in an MVC application you have to use asp.net MVC 6 (part of asp.net core or asp.net 5) only. But, with Web API you can use the older versions too. To setup the application please use the following steps:
Create an empty project using visual studio templates(don't select Web API or MVC)
Add the following Nuget packages to the project:
Microsoft.AspNet.WebApi
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.StaticFiles
Add a Startup.cs file and decorate the namespace with the following
[assembly: OwinStartup(typeof(Startup))]
Add the following code to the Stratup.cs class
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional}
);
//Configure the file/ static file serving middleware
var physicalFileSystem = new PhysicalFileSystem(#".\client");
var fileServerOptions = new FileServerOptions
{
EnableDefaultFiles = true,
RequestPath = PathString.Empty,
FileSystem = physicalFileSystem
};
fileServerOptions.DefaultFilesOptions.DefaultFileNames = new[] {"index.html"};
fileServerOptions.StaticFileOptions.ServeUnknownFileTypes = true;
fileServerOptions.StaticFileOptions.FileSystem = physicalFileSystem;
app.UseFileServer(fileServerOptions);
app.UseWebApi(config);
}
This should do the magic. Now you can host the application in IIS. IIS will serve the static assets only from client folder. Add Server folder and add controllers.
The Microsoft.Owin.Host.SystemWeb is what facilitates the hosting of Owin application in IIS. The file serve options help IIS to serve static files only from client folder.
Please let me know if you have any questions.
Based on your question, the project structure that you want to achieve should be like the following.
Basically you will have two folders only, Client and Server. Static files are served from client folder only. Server folder is not accessible. If this is what you need then it can be achieved easily with Owin Self Host with Static File Serving middleware.
Self host works with out any dependency on IIS. But, if your planning to host this application on Linux, you could use Asp.NET CORE 1.0. Later if you decide to host the application on IIS inside windows that can be achieved easily by adding the Microsot.Owin.Host.SystemWeb nuget package.
There are great blog posts on this topic. This is the link for one of them. Here is the link for achieving the same in Asp.NET Core.
I hope this solves your issues and please let me know if you have any questions.
Thank you,
Soma.

The best solution I found is to ignore asp.net normal way and write a new way
public override void Init()
{
BeginRequest -= OnBeginRequest;
BeginRequest += OnBeginRequest;
}
protected void OnBeginRequest(object sender, EventArgs e)
{
if (Request.Url.AbsolutePath.StartsWith("/endPoint"))
{
Context.RemapHandler(endPoint);
}
else
{
Context.RemapHandler(staticHandler);
}
}
Let endPoint and staticHandler implement IHttpHandler
it works but every static file moves through c# so there might be a solution with better performance

Related

implement Microsoft Authentication (owin) on an existing WebForms web app

I'm trying to implement Microsoft Authentication (owin) on an existing WebForms web application. I can't use the official guides from Microsoft, since they are made for modern solutions with MVC.
Any tips on where to start? I can't find any guides.
Please follow the document which helps you in old .NET forms to use OWIN Forms middleware and to use this middleware for Azure AD authentication please follow Docs.
Install the package for Microsoft.Owin.Security.Cookies
Right click the App_Start folder on your project Add -> New Item - > "OWIN Startup Class"
then create the following folder
public void Configuration(IAppBuilder app)
{
CookieAuthenticationOptions opt = new CookieAuthenticationOptions();
opt.AuthenticationType = CookieAuthenticationDefaults.AuthenticationType;// "Identity.Application";
opt.CookieName = ".SSO";
opt.CookieDomain = "localhost";
opt.SlidingExpiration = true;
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.1.96:6379");
IDataProtector proc = DataProtectionProvider.Create(new DirectoryInfo(#"C:\test\core"), buildAction =>
buildAction.SetApplicationName("MyApp").SetDefaultKeyLifetime(TimeSpan.FromDays(9000)).ProtectKeysWithDpapi().PersistKeysToStackExchangeRedis(redis)).CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Cookies", "v2");
DataProtectorShim shim = new DataProtectorShim(proc);
opt.TicketDataFormat = new AspNetTicketDataFormat(shim);
app.UseCookieAuthentication(opt);
}

.NET MVC DisplayModeProvider fallback

I am currently using DisplayModeProvider to check if a mobile request is coming in and serving up a Page.mobile.cshtml file if I detect a mobile request otherwise I'm serving the default page Page.cshtml. This also works as a fall-back - if there is a mobile request for PageX but PageX.mobile.cshtml does not exist but there is a PageX.cshtml, I serve PageX.cshtml. This is working as intended.
I would like to add to the fall-back behavior as I include support for tablet requests. So when a tablet device request is detected, if I have a Page.tablet.cshtml, it will go ahead and serve that file. If there isn't a ...tablet.cshtml file, I'd like it to try to serve the Page.mobile.cshtml file and if a Page.mobile.cshtml does not exist, we would serve the Page.cshtml file.
Is there a way to do this without having to create a ...tablet.csthml file for every page and Html.Partial'ing a ...mobile.cshtml within it?
You can do that by changing the route preference dynamically. Define the hierarchy as you want like tablet first then mobile and then web pages.
Here is a sample how CustomViewEngine can do that:
public class MyViewEngine : RazorViewEngine
{
public MyViewEngine()
: base()
{
ViewLocationFormats = new[] {
"~/Views/tab/{1}/%1/{0}.cshtml",
"~/Views/mobile/{1}/%1/{0}.cshtml",
"~/Views/{1}/%1/{0}.cshtml",
"~/Views/Shared/{0}.cshtml"
};
PartialViewLocationFormats = new[] {
"~/Views/tab/%1/{1}/{0}.cshtml",
"~/Views/mobile/%1/{1}/{0}.cshtml",
"~/Views/%1/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml"
};
}
}
Here the view will be searched in the /Views/tab/ folder first then /Views/mobile/ followed by /Views/ and the /Views/Shared/ folders.
Detail of the implementation have been discussed here: ASP.NET MVC Custom View Routing

Azure RoleEnvironment.Changing event not being called in ASP.NET MVC 5

I am trying to use the Azure Runtime Reconfiguration Pattern to allow me to change a appSetting in the normal Web.config file via PowerShell (later by Microsoft Azure Web Sites Management Library).
My problem is that the RoleEnvironment.Changing event is not being called in my MVC app, so the web app is being restarted. I have placed event set up code in the MVC Application_Start as described in the Azure article, i.e.
protected void Application_Start()
{
RoleEnvironment.Changing += RoleEnvironment_Changing;
RoleEnvironment.Changed += RoleEnvironment_Changed;
//normal MVC code etc...
AreaRegistration.RegisterAllAreas();
}
The event handlers are a straight copy of the handled from the Azure article and look like this:
private const string CustomSettingName = "TestConfig";
public static string TestConfigValue;
private static void RoleEnvironment_Changing(object sender,
RoleEnvironmentChangingEventArgs e)
{
RoleLogs.Add("RoleEnvironment_Changing: started");
var changedSettings = e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
.Select(c => c.ConfigurationSettingName).ToList();
Trace.TraceInformation("Changing notification. Settings being changed: "
+ string.Join(", ", changedSettings));
if (changedSettings
.Any(settingName => !string.Equals(settingName, CustomSettingName,
StringComparison.Ordinal)))
{
Console.WriteLine("Cancelling dynamic configuration change (restarting).");
RoleLogs.Add("RoleEnvironment_Changing: restarting!");
// Setting this to true will restart the role gracefully. If Cancel is not
// set to true, and the change is not handled by the application, the
// application will not use the new value until it is restarted (either
// manually or for some other reason).
e.Cancel = true;
}
else
{
RoleLogs.Add("RoleEnvironment_Changing: change is OK. Not restarting");
Console.WriteLine("Handling configuration change without restarting. ");
}
}
private static void RoleEnvironment_Changed(object sender,
RoleEnvironmentChangedEventArgs e)
{
RoleLogs.Add("RoleEnvironment_ChangED: Starting");
Console.WriteLine("Updating instance with new configuration settings.");
foreach (var settingChange in
e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>())
{
if (string.Equals(settingChange.ConfigurationSettingName,
CustomSettingName,
StringComparison.Ordinal))
{
// Execute a function to update the configuration of the component.
RoleLogs.Add("RoleEnvironment_ChangED: TestConfig has changed");
Console.WriteLine("TestConfig has changed.");
TestConfigValue = RoleEnvironment.GetConfigurationSettingValue(CustomSettingName);
}
}
}
I have added logs which prove that my RoleEnvironment_Changing and RoleEnvironment_Changed are not being called in the MVC WebApp which means the WebApp is restarted when I change an appSetting via PowerShell. This also means the RoleEnvironment.Changing event never gets to the WebJob.
I am using Azure SDK 2.7.0
Any ideas?
UPDATE
#richag gave me an answer, which made me realise that my problem is because I am using a App Service rather than a Cloud Service. This SO answer and plus this video (see at 5:00mins) talks about the difference (Note: the video is old so the name of the web app is different, but the concept is the same).
I don't really want to change this late in the development, and I have worked round the problem another way. Maybe on the next project and will look at Cloud Services as I can see some positives, like better control of my WebJobs configuration.
From the runtime reconfiguration pattern: "Microsoft Azure Cloud Services roles detect and expose two events that are raised when the hosting environment detects a change to the ServiceConfiguration.cscfg files" These events are not fired if you make changes to app.config/web.config files. Only when the cloud service configuration is changed, i.e. if you upload a new configuration file through the azure portal's configure tab or change a setting directly on the azure portal.
According to the debugger, none of the following events are fired when I update the Azure Portal to change an AppSetting for an ASP.NET WebAPI app:
RoleEnvironment.Changing
RoleEnvironment.Changed
RoleEnvironment.StatusCheck
RoleEnvironment.SimultaneousChanging
RoleEnvironment.SimultaneousChanged
RoleEnvironment.Stopping
Do others have different experience?

running express.js app in Asp.net MVC

I would like to run node.js with asp.net mvc for performing socket.io operations.
i've successfully included the node.js in asp.net mvc as described in here
my problem is how to run express.js in asp.net mvc,
i've performed the url rewritting in Global.asax.cs file like
void Application_BeginRequest(object sender, EventArgs e)
{
// Get current path
string CurrentPath = Request.Path.ToLower();
if (CurrentPath.StartsWith("/node"))
{ HttpContext MyContext = HttpContext.Current;
MyContext.RewritePath("/Node/index.js/");
}
}
so in the url http:localhost:1234/node should redirect me to the index.js file in Node folder
it is all working well but,
when i start coading express.js server in index.js like
var express = require('express');
var app = express.createServer();
app.get('/node/', function (req, res) {
res.send(' welcome to express ');
});
app.listen(process.env.PORT);
I got error saying Cannot GET /Node/index.js/
where am i missing? please guide me how to write express.js coading in asp.net mvc
Iam running this application in windows 7 32 bit system with IIS 8.0 express , and installed node versions are
iisnode.js - iisnode-full-iis7-v0.2.3-x86
node.js - node-v0.8.19-x86
Thank you.
Do you have a web.config entry for iisnode handling index.js?
iisnode is not set up as a .js handler by default, since there is far more client-side javascript than server-side javascript, so you need to explicitly turn on iisnode as a handler for that file.

ASP.NET MVC not using controller for explicit file route in IIS7

Consider a StaticResourceController that locates and serves files.
I've set up an explicit route for "favicon.ico" that will handle the request for this file using StaticResourceController:
routes.MapRoute(
"favicon",
"favicon.ico",
new { controller = "StaticResource", action = "Get", file = "favicon.ico", area="root"},
new[] { "Dimebrain.Mvc.Controllers" }
);
In IIS6 the expected result occurs when making a request for http://localhost:8080/favicon.ico.
Unfortunately when I deploy to IIS7 http://localhost/favicon.ico returns an IIS-generated 404, presumably because it's actually looking for the favicon.ico in the web root folder, where it doesn't exist.
I have enough happening in StaticResourceController that this isn't a good thing for my application, especially since it is multi-tenant and the favicon.ico file can change. I've set my web server modules to handle every request and override the RouteCollection to disregard file checks with RouteExistingFiles.
Why is the UrlRoutingModule getting in my way in IIS7 and forcing serving the static file from disk (404)?
In case anyone else runs into this problem, the solution is you need you to let MVC know not to process requests in folders where your actual static files live:
// Make sure MVC is handling every request for static files
routes.RouteExistingFiles = true;
// Don't process routes where actual static resources live
routes.IgnoreRoute("content/{*pathInfo}");
routes.IgnoreRoute("scripts/{*pathInfo}");
routes.IgnoreRoute("areas/admin/content/{*pathInfo}");
routes.IgnoreRoute("areas/admin/scripts/{*pathInfo}");
In adiition to Daniel Crenna's answer, you need to add in web.confug file in system.webServer section:
<modules runAllManagedModulesForAllRequests="true"/>

Resources