And still, what is the magic of ASP.NET MVC Content folder? - asp.net-mvc

I've just moved my resource files (javascript, css, images) from Content folder to custom Assets folder. And I've noticed a strange behavior - these files are not longer cached by browser and MvcMiniProfiler shows separate request for each resource located in Assets folder:
I know that Content folder isn't required by ASP.NET MVC convention, but why this happens? Is Content treated somehow especially by anyone (e.g. ASP.NET, IISExpress, etc.)? And how to force caching for other folders that contain static resources?
EDIT: Oh, it appears to be not an ASP.NET MVC odd behavior, but just the standard behavior of MvcMiniProfiler. Currently I'm checking that...
EDIT: Yea, there is no problem with ASP.NET MVC, it's just a default configuration of MvcMiniProfiler to ignore only these paths: "/mini-profiler-", "/content/", "/scripts/", "/favicon.ico". And these defaults can be easily extended:
MiniProfiler.Settings.IgnoredPaths = MiniProfiler.Settings.IgnoredPaths
.Concat(new [] { "/assets/" })
.ToArray();
Sometimes it's a good idea to read documentation before using something ;)

As you're indicating in your update, this appears to be a feature of MvcMiniProfiler:
/// <summary>
/// When <see cref="MiniProfiler.Start"/> is called, if the current request url contains any items in this property,
/// no profiler will be instantiated and no results will be displayed.
/// Default value is { "/mini-profiler-", "/content/", "/scripts/", "/favicon.ico" }.
/// </summary>
[DefaultValue(new string[] { "/mini-profiler-", "/content/", "/scripts/", "/favicon.ico" })]
public static string[] IgnoredPaths { get; set; }
Source.
Presumably, the images were never cached when you were serving them through Cassini, because Cassini is terrible at that (passing png files as application/octet-stream, for instance), but the issue was manually hidden from your view by MvcMiniProfiler.

This is a strange behavior. However, put the following code inside your web.config file which is under the root of your app:
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00" />
</staticContent>
</system.webServer>
This code appends the necessary response headers in order for browser caching to work. You can tweak the time of course. For further info please refer : http://www.iis.net/ConfigReference/system.webServer/staticContent/clientCache

Related

TempData /Session in MVC4 / IIS6 not returning stored values

Really struggling with an MVC / TempData / Session / possibly IIS6 type issue.
I've got a simple MVC website - primary controller looks like this:
public class DemoController : Controller
{
[HttpPost]
public RedirectToRouteResult Index(SomeObject obj)
{
_someService.DoStuffWith(obj);
TempData.Add("SomeObject", obj);
return RedirectToAction("Index");
}
[HttpGet]
public ViewResult Index()
{
var obj = TempData.Peek("SomeObject") as SomeObject;
return View("Hello", obj);
}
}
So this is a mega-simple Post Redirect Get - submit data from somewhere, get it in the Post-friendly action, do stuff with it, poke it into temp data, redirect to Get-friendly, pick it up again, stick the user on a new view with the object in the model. Have implemented this a dozen times before, and never had a problem, but it's always been on IIS7.
This works exactly as expected when running locally, both on cassini, and on local IIS 7.5. However, as soon as I deploy to Server 2003 and IIS6, the first time I try to access any property of the "SomeObject" model in the view, I get a null reference exception.
So, what else have I tried:
I've ditched TempData and just pushed it into Session["Whatever"] - with same results
I've pushed into HttpContext.Current.Session["Whatever"] - with same results
I've pushed into ControllerContext.HttpContext.Session["Whatever"] - with same results
(Admittedly, i'm sketchy on the differences between these - have never needed to worry about it before now)
I've made sure session state is switched on on IIS.
I've checked that the Asp.net session identifier is not changing between requests.
I've deployed to 2 separate servers, both running 2003 and IIS6, same results on both
Explicitely used SessionStateBehaviour.Required
What i'm not able to do:
Deploy to IIS7, outside my local machine
Use SqlServer SessionState
Use StateServer SessionState
Also worth mentioning that this site is using the MVC4 RC - though the RC features are not used in this particular section of the site.
Any ideas or comments are most welcome!
Thanks.
This was the problem on my system - I had a server name that contained underscores. Underscores are not allowed in host names by RFC 952 and may interfere with the ability to set cookies and thus to persist sessions.
To track this down I started watching the SessionID in each controller section and noticed that with every post it was changing. This was only happening on our test server because we named it with _t to show it was a test machine. OUCH!
I found this point.
That may offer other value.
I hope this helps
David
The problem for me was I had this set in my web.config file, but wasn't using SSL:
<httpCookies requireSSL="true" />
This setting prevents cookies from working over unsecure (i.e. non-SSL) connections - including the ASP.NET session cookie.

ASP.NET MVC VirtualPathProvider not working under IIS 6

By ASP.NET MVC plugin architecture, Plug-in architecture for ASP.NET MVC
I have separated DLL(plugin) which contains the views, css and javascript files in the resources. So my own VirtualPathProvider will load the content out from the DLL if that is for the plugin. It works all fine during development. But It appears not working once I deployed it in IIS. (I mapped the whidcard in IIS 6 and the views are showing)
I have registered my VirtualPathProvider in global.asax as
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
}
For example.
http://localhost/Plugin/MyPlugin.dll/Styles.MyStyles.css
This should be loaded from the plugin.dll but IIS returns 404.
I guess the static files are all handled by the IIS and not went through asp.net and my VirtualPathProvider ? Is there way to get around this? Please shed some light.
Thanks in advance.
If this is IIS 6 you will need a wildcard mapping. See this blog post from Phil Haack.
I've found the workaround by adding the staticFileHandler in the web.config httpHandlers element.
<add verb="GET,HEAD,POST" path="*" type="System.Web.StaticFileHandler" validate="true" />
I've had a number of problems getting an external compiled library containing resources and controllers to work in our MVC environment. It's used across multiple projects and different errors have surfaced in different projects so here's all the things I had to do (so far) to ensure static file handling works:
Include StaticFileHandler in web.config, e.g.:
<add verb="GET,HEAD" path="*.js" name="Static for js" type="System.Web.StaticFileHandler" />
Ensure Static items are ignored in routing:
routes.IgnoreRoute("{*staticfile}", new { staticfile = #".*\
.(css|js|gif|jpg)(/.*)?" });
Register a Virtual Path Provider, e.g.:
System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedResourceVirtualPathProvider.Vpp(assemblies.ToArray())
{
//you can do a specific assembly registration too. If you provide the assemly source path, it can read
//from the source file so you can change the content while the app is running without needing to rebuild
//{typeof(SomeAssembly.SomeClass).Assembly, #"..\SomeAssembly"}
});
Not required for static files but worth mentioning are what was needed to get the views / controllers working, which was adding MVCContrib and registering the embedded view engine:
PortableAreaRegistration.RegisterEmbeddedViewEngine();

Asp.net MVC and normal html pages

I'm about to create quite simple website which will contain several static pages (they will never change) and one dynamic change (let's call it news). I'was wondering whether it's possible to use MVC here without having to create controllers and views for these "static" pages. Isn't that just too much overhead?
Is there a way to make MVC simply route incoming requests to valid documents without actually having to create controller?
Just put your static content in a separate directory and link to it there. ASP.NET will simply serve up the static content as normal when the path is to a actual file. I created a static folder in my Content folder, but you could put it anywhere. The files could even live in the root of the site.
+-Content
+-images
+-static
+-about.html
+-info.html
+-styles
+-site.css
+-themes
...
For "static" aspx files, you would need to wire up a route (or use the default catch all) to something like:
public SomeAction ActionResult(string pageName)
{
return View(pageName);
}
And that should let someone make views in the appropriate folder and then have them be added and or executed on the fly.

Exclude HttpModule from running for static content on IIS7

I have a problem with my Authentication HttpModule. The problem is that it obviously runs for every single request I get on my web server (IIS7). Because it also uses Session variable, it fails to work properly on CSS, JS files and similar.
I tried to use:
<add name="AuthModuleName" type="..." preCondition="managedHandler" />
but to no avail. It still runs on every request regardless of its extension or mime type. I should also add, there's a setting
<modules runAllManagedModulesForAllRequests="true">
that seemed suspicious to me and actually disabled preConditions on modules. But changing it to false, breaks the application in a completely different way and with a different exception (The SessionStateTempDataProvider requires SessionState to be enabled).
Can anybody help me how to force IIS7 to exclude my HttpModule when requests are made for static content files?
runAllManagedModulesForAllRequests attribute has to be set to false to actually configure any module the way you want. You will have to also correctly reconfigure Session and others as needed, but the main thing is handlers pipeline execution order that handles requests.
The answer was provided in one of my other questions:
Thanks to Peter that provided the answer that worked correctly.
I don't know about an IIS7 setting for that but you can do this.
The session object will be available only for non-static content :
void yourEventHandler(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
if (app.Context.Session == null) {
return;
}
// then your code here...
}
This will ensure your code won't be run for files like CSS, JS etc. But keep in mind the session object will also not be ready before PostAcquireRequestState event. (For the order of the HttpApplication events, see this page.)
Edit : Also, it appears with ASP.NET Development Server (though I know you said IIS7 in your question), your HttpModule will still run even for static files.

Issues During ASP.NET MVC Upgrade from Preview 5 to Beta?

What issues or refactoring did you have to do when you upgraded from ASP.NET MVC Preview 5 to the newly released Beta version?
Issue number one: Yellow screen of death.
CS0234: The type or namespace name 'Mvc' does not exist in the namespace 'System.Web' (are you missing an assembly reference?)
Solution: I removed all references in my project and re-added them, pointing to the assemblies in program files\asp.net\asp.net mvc beta\assemblies, but that didn't solve the problem.
I had a system.web.mvc dll in the gac (no idea how). Tried to delete it. Unable to; assembly is required by one or more applications. Had to find the assembly as described here and delete the registry entry. I was then able to remove the gac's version of system.web.mvc.
This STILL didn't fix the problem. I had to RE-ADD the references AGAIN. Now its working.
Just to be clear!!! The beta assemblies were dropped under Program Files, while an older version of System.Web.Mvc was in the GAC.
I'm about to do this myself. Here's the list of changes from the readme:
Changes Made Between CodePlex Preview 5 and Beta
Changed the default validation messages to be more end-user friendly.
Renamed CompositeViewEngine to AutoViewEngine.
Added a Url property to Controller of type UrlHelper. This makes it convenient to generate routing-based URLs from within a controller.
Added the ActionNameSelectorAttribute abstract base class, which serves as the base type for ActionNameAttribute. By inheriting from this base attribute class, you can create custom attributes that participate in action selection by name.
Added a new ReleaseView method to IViewEngine that allows custom view engines to be notified when a view is done rendering. This is useful for cleanup or for view-pooling scenarios.
Renamed the ControllerBuilder method DisposeController to ReleaseController to fit with the pattern that is established for view engines.
Removed most of the methods on the HtmlHelper class, converting them to extension methods of the HtmlHelper class instead. These methods exist in a new namespace (System.Web.Mvc.Html). If you are migrating from Preview 5, you must add the following element to the namespaces section of the Web.config file:
<add namespace="System.Web.Mvc.Html"/>
This makes it possible for you to completely replace our helper methods with your own.
Changed the default model binder (DefaultModelBinder) to handle complex types. The IModelBinder interface has also been changed to accept a single parameter of type ModelBindingContext.
Added a new HttpVerbs enumeration that contains the most commonly used HTTP verbs (GET, POST, PUT, DELETE, HEAD). Also added a constructor overload to AcceptVerbsAttribute that accepts the enumeration. The enumerated values can be combined. Because it is possible to respond to HTTP verbs that are not included in the enumeration, the AcceptVerbsAttribute retains the constructor that accepts an array of strings as a parameter. For example, the following snippet shows an action method that can respond to both POST and PUT requests.
[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Put)]
public ActionResult Update() {...
}
Modified the RadioButton helper method to ensure that every overload accepts a value. Because radio buttons are used to specify a choice from a set of possible values, specifying a value for a radio button is necessary.
Made modifications and fixes to the default project template. This includes moving script files to a new Scripts folder. The default template uses the ModelState class to report validation errors.
Changed action-method selection. If two action methods match a request, but only one of those has an attribute that derives from ActionMethodSelectorAttribute that matches the request, that action is invoked. In earlier releases, this scenario resulted in an exception.
For example, the following two action methods are in the same controller:
public ActionResult Edit() {
//...
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection form) {
//...
}
In Preview 5, a POST request for the Edit action would cause an exception, because two methods match the request. In the Beta, precedence is given to the method that matches the current request via the AcceptVerb attribute. In this example, the first method will handle any non-POST requests for the Edit action.
Added an overload for the ViewDataDictionary.Eval method that accepts a format string.
Removed the ViewName property from the ViewContext class.
Added an IValueProvider interface for value providers, along with a default implementation, DefaultValueProvider. Value providers supply values that are used by the model binders when binding to a model object. The UpdateModel method of the Controller class has been updated to allow you to specify a custom value provider.
I experienced the same problem as Will and had to do similar things as him, including copying the dlls to the bin folder.
Now things are working in the internal vs.net server but are causing IIS7 to crash.
Ok, it turns out one of the major problems is that I missed the step to update the compilation assemblies in the web.config:
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
All i had to do was update the assemblies from
%ProgramFiles%\Microsoft ASP.NET\ASP.NET MVC Beta
Also get the most recent Microsoft.Web.MVC from codeplex
to update my futures assembly too.
add in 2 lines to the web.config
This one to the <assemblies> Section:
<add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
This one to the <namespaces> section:
<add namespace="System.Web.Mvc.Html"/>
Then i had to update all the <%using (Html.Form()) to <%using (Html.BeginForm())
On one code file i had to add the System.Web.Mvc.Html; namespace
My stuff is based on Rob Conery's MVC Storefront, so anyone using that should be able to follow the above.
Hope it helps someone out there.
Disregard this... I'm a loser - it's Microsoft ASP.net in program files... not just ASP.net
Maybe this should be a second question, but I think keeping it all in one place might help.
When running the Beta installer nothing ends up changing on my PC. I don't see the folder in the Program Files folder... no assemblies are added to the GAC... even the installer gets to the last step and then hangs for around 10 minutes or so.
I've uninstalled and reinstalled a couple times now without any luck.
Anyone having a similar problem?
The problem with AutoFac has now been resolved in Revision 454 of the AutoFac code base
http://code.google.com/p/autofac/issues/detail?id=86&can=1
Im trying to find out how the new ModelBinder works, as far as I can see it's very different, but i haven't managed to find out how it works yet..
My old looked like:
public class GuestbookEntryBinder : IModelBinder
{
#region IModelBinder Members
public object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState)
{
if (modelType == typeof(GuestbookEntry))
{
return new GuestbookEntry
{
Name = controllerContext.HttpContext.Request.Form["name"] ?? "",
Website = controllerContext.HttpContext.Request.Form["website"] ?? "",
Message = controllerContext.HttpContext.Request.Form["message"] ?? "",
};
}
return null;
}
#endregion
}
The new one looks like:
#region IModelBinder Members
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
throw new NotImplementedException();
}
#endregion
Any hints?
I use Autofac as my DI container. A null container exception gets thrown when trying to dispose of the container objects.
Yup, also use Autofac as DI container.
Get same issue as this guy
http://groups.google.com/group/autofac/browse_thread/thread/68aaf55581392d08
No idea if a fix is possible but cant continue until this is fixed ......
After struggling with this for most of the day, I figured I'd post my solution here. Maybe this is normal Visual Studio behavior but I never noticed it before...
On my existing project, I actually had to manually move the Beta files to the Bin folder. For whatever reason, just browsing to it with Add Reference wasn't working...
Html.TextBox - value now is object, not string.
So, hidden errors possible (not at compile time and even not at runtime), for example I've used this overloaded method earlier Html.TextBox(string name, object htmlAttributes). Now my attrs go into textbox value.
About the Autofac issue. There is a thread on the autofac discussion group about the need to update the controller factory to be compatible with the Beta release of the MVC framework
http://groups.google.com/group/autofac/browse_thread/thread/68aaf55581392d08
I hope they post a new version very very soon :-)
When I upgraded from Preview 5 to Beta I had difficulty locating the generic overloads of ActionLink. It appears that those are not included in the main release of ASP.NET MVC but are being shipping as "futures".
I found the necessary assembly (Microsoft.Web.Mvc) # http://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=aspnet&ReleaseId=18459
There is a breaking change in the ViewContext constructor. It has changed from:
ViewContext(ControllerContext context, string viewName, ViewDataDictionary viewData, TempDataDictionary tempData)
to:
ViewContext(ControllerContext context, IView view, ViewDataDictionary viewData, TempDataDictionary tempData)
This broke my code because I am using MvcContrib.Services.IEmailTemplateService, which takes a ViewContext in its RenderMessage method. To get an IView from the template name, I am doing the following:
var view = ViewEngines.DefaultEngine.FindView(controllerContext, viewName, null);
Not sure if this is the best practice, but it seems to work.
This is now broken:
<%=Html.TextBox("Name", new Hash(#class => "required"))%>
In Preview 5 the above would bind the value of ViewData.Model.Name to the textbox. This still works:
<%=Html.TextBox("Name")%>
But if you want to specify html attributes, you must also specify the value as follows:
<%=Html.TextBox("Name", ViewData.Model.Name, new Hash(#class => "required"))%>
Actually this is not really safe. If there is any chance ViewData.Model might be null you need to do something like this:
<%=Html.TextBox("Name", ViewData.Model == null ? null : ViewData.Model.Name, new Hash(#class => "required"))%>
This change seems counter to the Beta release notes:
"...in order to reduce overload
ambiguity...the value parameter was changed
from object to string for several
helper methods."
The value parameter for TextBox used to be string, and it was changed to object. So to avoid ambiguities they had to remove the one overload that I use the most. :(
IMHO, every HTML helper method should have overloads that allow binding in all cases without specifying the value. Otherwise we will end up with inconsistent view code that will confuse future devs.
If you are using Html.Form from the futures assembly (Microsoft.Web.Mvc) you might get a name collision on the FormMethod enum. For example:
Html.Form<FooController>(c => c.Bar(), FormMethod.Post, new Hash(#class => "foobar"))
This will complain that FormMethod is an ambiguous reference between Microsoft.Web.Mvc and System.Web.Mvc. This is quite sad because IMHO BeginForm does not provide a viable option due to its lack of an override that uses a lambda expression. Your only option is to use magic strings, which resist refactoring.
The best solution, it seems, is to put the following into every view that uses FormMethod:
<%# Import Namespace="FormMethod=Microsoft.Web.Mvc.FormMethod"%>
Ugh. Hopefully this is temporary. I expect that the futures assembly can be changed to use the enum from System.Web.Mvc. Or much better yet, hopefully they overload BeginForm to use expressions.
It seems that Html.Image is broken. As of preview 5 it was moved to the futures assembly. I cannot imagine why. Anyway, the error is:
Method not found: 'Void System.Web.Mvc.UrlHelper..ctor(System.Web.Mvc.ViewContext)'
The best solution I can see is to replace this:
<%=Html.Image("~/Content/Images/logo.jpg") %>
with this:
<img src="<%=Html.ResolveUrl("~/Content/Images/logo_350.jpg")%>" />
What Will said above, except that in addition to deleting the assemblies from the GAC and re-adding the references I also had to run the Beta installer again (putting the right assemblies in the GAC this time, though I'm just using a file reference).
I suspect if I'd deleted the Preview 5 assemblies from the GAC (and I've no idea how they got in there either) before I ran the installer, everything might have been OK. Worth trying.
In the unlikely event that anyone else out there is as daft as me and working on Vista, you may not need to do the registry hacking above in order to delete the old assemblies - just run gacutil from an admin command prompt. Doh!
I found that updating the web.config namespaces element with the namespaces from a blank project fixed my problems. I also had to update my ModelBinders due to the interface change.

Resources