Changing dependency of WebSharper.JQueryUI to load jquery-ui.js locally - jquery-ui

I'm writing on a WebSharper sitelet that uses the JQueryUI extension. The HTML generated by the WebSharper sitelet looks like this:
<html>
<head>
...
<script src="//code.jquery.com/ui/1.11.1/jquery-ui.js" ...></script>
I'm frequently without internet while developing, so I'd really like to serve jquery-ui.js off the development server instead. That is, I'd much rather have this:
<html>
<head>
...
<script src="/Scripts/jquery-ui.js" ...></script>
The docs say this should be possible by setting an appropriate appSetting in Web.config, but no value I set for the keys listed in the docs seem to have any effect on the output.
I'm using (NuGet versions) WebSharper 3.0.54.140 and WebSharper.JQueryUI 3.0.45.241.
How do I force WebSharper to output a link to a resource local to the server?

Indeed there is an error in the documentation. The key to use is the fully qualified name of the resource type, so for WebSharper.JQueryUI it should be:
<appSettings>
<add key="WebSharper.JQueryUI.Dependencies+JQueryUIJs" value="/Scripts/jquery-ui.js" />
<add key="WebSharper.JQueryUI.Dependencies+JQueryUICss" value="/Content/jquery-ui.css" />
</appSettings>
Edit: just fixed the documentation.

Related

IIS Express with portnumbers

I have a ASP.Net MVC project in maintenance. Apparantely the IISExpress is adding portnumbers to localhost which is fine. except when the relative style sheet link does not contain the portnumbers?
How come the portnumbers are not added automaticaly? Seems pretty logical to me that when Visual Studio 2013 starts up it sends the browser to localhost with portnumber but when building the relative style sheet links it does not?
Guy
In view file:
<link rel="stylesheet" href="./metrouicss.css" />
After running in IISexpress:
<link rel="stylesheet" href="http://localhost//Templates/Design 2/metrouicss.css" />
Since IISexpress is running on port 42532 I would rather see it rendered like this:
<link rel="stylesheet" href="http://localhost:42532//Templates/Design 2/metrouicss.css" />
Sorry try this (I missed the rest of the path):
<link rel="stylesheet" href="~/Templates/Design 2/metrouicss.css" />
Or
If you are using MVC 5 you can use the BundleConfig class:
Public Sub RegisterBundles(ByVal bundles As BundleCollection)
bundles.Add(New StyleBundle("~/Templates/css").Include(
"~/Templates/Design 2/metrouicss.css"))
End Sub
And in view use the following in the tags:
#Styles.Render("~/Templates/css")

what is the difference between grails link ,createlink and resource tags

Is there any difference between grails link ,create link and resource tags and I want to know when to use each tag as per my knowledge the createlink tag has depricated
it's simple:
g.createLink generates only the url, like /aaa/bbb/222, based on controller/action/params for example
g.link generated the <a>-HTML-tag, using the g.createLink to generate the url to be put into the #href attribute
g.resource outputs a path to a resource file, available under web-app folder
<link rel="stylesheet" href="${resource(dir:'css',file:'style.css')}" />
produces
<link rel="stylesheet" href="/css/style.css" />
CREATE LINK is soemthing power full when u come to knowing the absolute or relative path in using likes inside a gsp page.
Let assume i have the following path /yourapp/controller1/view1
Instead of using
<a href="{grailscontext.thensomebaseurlstuff}/"+controller/view /> ,
which fails according to some context using the below will make it easy.
my link
<g:createLink url="[action:'list',controller:'book']" />
And ,glink is the above implementation using taglib form.. does the same effect as above but being in taglib makes reduce some computation or create some am not sure.
g.link("text of the link here", action:"foo", controller:"bar")
ResourceTags is no wonder its something help full and important in attaching either an image,css and js folder/file resource into a grails application.
// generates "/shop/css/main.css"
<g:resource dir="css" file="main.css" />
// generates "http://portal.mygreatsite.com/css/main.css"
<g:resource dir="css" file="main.css" absolute="true" />
// generates "http://admin.mygreatsite.com/css/main.css"
<g:resource dir="css" file="main.css" base="http://admin.mygreatsite.com"/>
Alloha!

Facing issue while trying to Cache images js files using Combres Nuget Package

Below is my Fidler.
If you pay attention to the above screenshot, js and css files are being downloaded on every refresh. Why?
I have a query about the functionality of Combres. Url.Combress can cache the css files. right? In case you remove the css file from the physical location. I get 404 error? Why? Because this file is cached. So it should not be picked from it's physical location. Instead it should be picked from cache. Correct?
Explanation
I am using MVC3. I have installed Nuget Package combres in Package Manager Console
Install-Package combres.mvc
Below is the proof of my Route Table
I have below two files in my Layout.
<link href=""~/Content/Site.css" type="text/css" />
<script src="~/Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
Combress.xml setting
<combres xmlns='urn:combres'>
<filters>
<filter type="Combres.Filters.FixUrlsInCssFilter, Combres" />
</filters>
<resourceSets url="~/combres.axd"
defaultDuration="30"
defaultVersion="auto"
defaultDebugEnabled="auto"
defaultIgnorePipelineWhenDebug="true"
localChangeMonitorInterval="30"
remoteChangeMonitorInterval="60"
>
<resourceSet name="siteCss" type="css">
<resource path="~/content/Site.css" />
</resourceSet>
<resourceSet name="siteJs" type="js">
<resource path="~/scripts/jquery-1.7.1.min.js" />
</resourceSet>
</resourceSets>
</combres>
When i execute the below path
http://localhost:2474/Home/About
I think, it is downloading the above files every time. As per my understanding, combress should Cache the images/css/js file which ever files are mentioned in the combres.xml setting.
You need to reference the files differently. Since you are using asp.net-mvc I would recommend you also install this complementary nuget package
Install-Package combres.mvc
And then in your master page, or wherever you want your different resources
#Url.CombresLink("siteCss")
#Url.CombresLink("siteJs")
Note : Before adding the above code lines. Add #using Combres.Mvc; in your layout Page.
This is the standard recommendation as per the documentation.
Note that you probably want to have full source in debug mode and only combine in release mode, which is a setting on your resourceSet: defaultDebugEnabled="auto"
There are other considerations but that should be enough to get started.

Why is my CSS bundling not working with a bin deployed MVC4 app?

I have bin deployed an MVC4 application to my hosting provider, based on advice given here and one or two on-the-fly fixes, but the most immediately apparent problem is that the bundling for css doesn't work. When I replace the bundle ref with explicit file refs, my css works again.
I am using a standard MVC4 RTM project template from VS2012. The provider is running IIS 7.5 and ASP.NET 4, and my previous MVC3 version of the same app worked fine. I am guessing I have grabbed a dependency somewhere of too low a version, and this might also contribute to my area based action link problem.
Technical symptoms are:
The line #Styles.Render("~/Content/css") renders as <link href="/Content/css?v=" rel="stylesheet"/>
UPDATE 11/4/2013:
The reason why this happens is because you have .js or .css at the end of your bundle name which causes ASP.NET to not run the request through MVC and the BundleModule.
The recommended way to fix this for better performance is to remove the .js or .css from your bundle name.
So /bundle/myscripts.js becomes /bundle/myscripts
Alternatively you can modify your web.config in the system.webServer section to run these requests through the BundleModule (which was my original answer)
<modules runAllManagedModulesForAllRequests="true">
<remove name="BundleModule" />
<add name="BundleModule" type="System.Web.Optimization.BundleModule" />
</modules>
Edit: I also noticed that if the name ends with 'css' (without the dot), that is a problem as well. I had to change my bundle name from 'DataTablesCSS' to 'DataTablesStyles' to fix my issue.
The CSS and Script bundling should work regardless if .NET is running 4.0 or 4.5. I am running .NET 4.0 and it works fine for me. However in order to get the minification and bundling behavior to work your web.config must be set to not be running in debug mode.
<compilation debug="false" targetFramework="4.0">
Take this bundle for jQuery UI example in the _Layout.cshtml file.
#Styles.Render("~/Content/themes/base/css")
If I run with debug="true" I get the following HTML.
<link href="/Content/themes/base/jquery.ui.core.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.resizable.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.selectable.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.accordion.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.button.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.dialog.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.slider.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.tabs.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.datepicker.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.progressbar.css" rel="stylesheet"/>
<link href="/Content/themes/base/jquery.ui.theme.css" rel="stylesheet"/>
But if I run with debug="false". I'll get this instead.
<link href="/Content/themes/base/css?v=myqT7npwmF2ABsuSaHqt8SCvK8UFWpRv7T4M8r3kiK01" rel="stylesheet"/>
This is a feature so you can easily debug problems with your Script and CSS files. I'm using the MVC4 RTM.
If you think it might be an MVC dependency problem, I'd recommend going into Nuget and removing all of your MVC related packages, and then search for the Microsoft.AspNet.Mvc package and install it. I'm using the most recent version and it's coming up as v.4.0.20710.0. That should grab all the dependencies you need.
Also if you used to be using MVC3 and are now trying to use MVC4 you'll want to go into your web.config(s) and update their references to point to the 4.0 version of MVC. If you're not sure, you can always create a fresh MVC4 app and copy the web.config from there. Don't forget the web.config in your Views/Areas folders if you do.
UPDATE: I've found that what you need to have is the Nuget package Microsoft.AspNet.Web.Optimization installed in your project. It's included by default in an MVC4 RTM app regardless if you specify the target framework as 4.5 or 4.0. This is the namespace that the bundling classes are included in, and doesn't appear to be dependent on the framework. I've deployed to a server that does not have 4.5 installed and it still works as expected for me. Just make sure the DLL gets deployed with the rest of your app.
Just to clarify a few things, System.Web.Optimization (aka Bundling/Minification) will work against 4.0. It is not depending on anything in 4.5, so there should be no problems there.
If script bundling is working, and its only an issue with CSS, perhaps the issue is with relative URLs?
I'd first look at the rendered page and see if you are getting references to the CSS bundle, i.e. something like:
<link href="/app/Content/css?v=oI5uNwN5NWmYrn8EXEybCI" rel="stylesheet"/>
If you are, then bundling is working, but something inside your CSS bundle is messed up. Usually this is due to relative URLs inside your CSS bundle being incorrect, i.e. if your images live under ~/Content, but you name your bundle ~/bundles/css, the browser will incorrectly look for images under ~/bundles.
Also, the default behavior is to disable bundling and minification when debug=true. So, if you do want optimizations enabled even when debug=true, you will need to force:
BundleTable.EnableOptimizations = true
Updated: With the new info that v="", that means the bundle was empty, you should verify that you are adding files to the bundle correctly, and that it found them. How are you including files to the bundle?
Omitting runAllManagedModulesForAllRequests="true" also worked for me. Add the following configuration in web.config:
<modules>
<remove name="BundleModule" />
<add name="BundleModule" type="System.Web.Optimization.BundleModule" />
</modules>
runAllManagedModulesForAllRequests will impose a performance hit on your website if not used appropriately. Check out this article.
Another thing to consider is the references cannot have the same name. For example, if you have jQuery UI in the libraries directory, and bundle its JavaScript file like so:
bundles.Add(new ScriptBundle("~/libraries").Include("~/libraries/jquery-ui/jqyery-ui.js"));
and then try to bundle its CSS files like so:
bundles.Add(new StyleBundle("~/libraries").Include("~/libraries/jquery-ui/jqyery-ui.css"));
...
it will fail. They have to have unique names. So do something like ScriptBundle("~/libraries/js")... and ScriptBundle("~/libraries/css")... or whatever.
As an addendum to the existing answers, none of which worked for me I found my solution HERE:
https://bundletransformer.codeplex.com/discussions/429707
The solution was to
right click the .less files in visual studio
Select properties
Mark the Build Action as Content
Redeploy
I did NOT need to remove extensionless handlers (as can be found in other solutions on the internet)
I did NOT have to add the <add name="BundleModule" type="System.Web.Optimization.BundleModule" /> web.config setting.
Hope this helps!
I encountered the same issue with CSS on a live environment. I followed all of the advise here and investigated how the bundling works behind the scene. This lead me to request that the .Net cache was cleared (I didn't have access to the app servers) which caused the bundling to start working on the app servers. However, when accessing the site via a load balancer with a CDN configured, although the bundle identifier was updated in the url, the bundle contained the old CSS. Simply flushing the CDN resolved the issue.
I hope this goes some way to helping some one else who may encounter this
One of my css files had an '_' character in the file name which caused issues.
Renamed your_style.css to yourstyle.css
I know this is an old issue, but people may still face this.
The following checks if the BundleModule exists in web.config and loaded,
and sets EnableOptimizations based on its existance.
This way wether it is available or not, the css/js references will work fine.
In other words:
If BundleModule is available, the bundeling/optimization will be enabled.
If BundleModule is not available, the bundeling/optimization will be disabled and automatically the full references will be used
instead.
Code:
public static void RegisterBundles(BundleCollection bundles)
{
// bundeling code here
// ...
// bundeling code here
bool bundelingModuleIsAvailable = false;
try {
bundelingModuleIsAvailable = HttpContext.Current.ApplicationInstance.Modules.AllKeys.Contains("BundleModule");
}
catch { }
if (!bundelingModuleIsAvailable)
System.Diagnostics.Debug.WriteLine("WARNING : optimization bundle is not added to Web.config!");
BundleTable.EnableOptimizations = bundelingModuleIsAvailable && !Debug_CheckIsRunning();
//Debug_CheckIsRunning is optional, incase you want to disable optimization when debugging yourself
BundleTable.EnableOptimizations = true;
}
private bool Debug_CheckIsRunning()
{//Check if debug is running
string moduleName = System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName;
return (moduleName.Contains("iisexpress.exe") || moduleName.Contains(".vshost") || moduleName.Contains("vstest.executionengine") || moduleName.Contains("WebDev.WebServer"));
}
Just for history:
Check that all mentioned less/css files in bundle have Build Action = "Content".
There is no error if some files from bundle missing on destination server.
I had the same problem and it turned out to be a stupid mistake, the css files were not included in the project so they weren't published, make sure you view all files in the solution and add them to the project.
I was facing this problem while deploying the code in Azure websites. it did worked when I deployed build from visualstudio.com and wasn't when I tried to publish the build from visual studio 2013. the main problem was CSS Minification. I totally agree with 1st response to this question. but thought of sharing solution that worked for me, may be it will help you in fixing it.
basically when we deploy through VSO it generates minification files for css, js by kicking in system.web.Optimization, but when we do publish build from VS 2013 we have to take care of the below.
bundling-and-minification
1. make sure the folder structure and your bundler naming convention should be different. something like this.
bundles.Add(new StyleBundle("~/Content/materialize/mcss").Include(
"~/Content//materialize/css/materialize.css"));
2. add at the bottom of your bundleconfig definition
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
BundleTable.EnableOptimizations = true;
}
3. make sure to add debug false in web.config (when you start local debuging VS2013 will give you a popup saying that you need to make sure to putting it back before deploying into prod. that itself explains much.
<system.web>
<compilation debug="true" />
<!-- Lines removed for clarity. -->
</system.web>
With Visual Studio 2015 I found the problem was caused by referencing the .min version of a javascript file in the BundleConfig when debug=true is set in the web.config.
For example, with jquery specifying the following in BundleConfig:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.min.js"));
resulted in the jquery not loading correctly at all when debug=true was set in the web.config.
Referencing the un-minified version:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include("~/Scripts/jquery-{version}.js"));
corrects the problem.
Setting debug=false also corrects the problem, but of course that is not exactly helpful.
It is also worth noting that while some minified javascript files loaded correctly and others did not. I ended up removing all minified javascript files in favor of VS handling minification for me.
try this:
#System.Web.Optimization.Styles.Render("~/Content/css")
#System.Web.Optimization.Scripts.Render("~/bundles/modernizr")
It worked to me.
I'm still learning, and I've not figured it out yet why it is happening
To add useful information to the conversation, I came across 404 errors for my bundles in the deployment (it was fine in the local dev environment).
For the bundle names, I including version numbers like such:
bundles.Add(new ScriptBundle("~/bundles/jquerymobile.1.4.3").Include(
...
);
On a whim, I removed all the dots and all was working magically again:
bundles.Add(new ScriptBundle("~/bundles/jquerymobile143").Include(
...
);
Hope that helps someone save some time and frustration.
I had this issue while adding some packages from nuget and forgot to do an update
So first do an update of all packages installed in the project
Update-Package
In the Global.asax.cs add the following
BundleTable.EnableOptimizations = true;
css names must be consistent.
Names of jqueryUI css are not "jquery.ui.XXX" in Contents/themes/base folder .
so it should be :
wrong:
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
correct :
bundles.Add(new StyleBundle("~/Bundles/themes/base/css").Include(
"~/Content/themes/base/core.css",
"~/Content/themes/base/resizable.css",
"~/Content/themes/base/selectable.css",
"~/Content/themes/base/accordion.css",
"~/Content/themes/base/autocomplete.css",
"~/Content/themes/base/button.css",
"~/Content/themes/base/dialog.css",
"~/Content/themes/base/slider.css",
"~/Content/themes/base/tabs.css",
"~/Content/themes/base/datepicker.css",
"~/Content/themes/base/progressbar.css",
"~/Content/themes/base/theme.css"));
I tried in MVC5 and worked successfully.
You need to add this code in your shared View
#*#Scripts.Render("~/bundles/plugins")*#
<script src="/Content/plugins/jQuery/jQuery-2.1.4.min.js"></script>
<!-- jQuery UI 1.11.4 -->
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<!-- Kendo JS -->
<script src="/Content/kendo/js/kendo.all.min.js" type="text/javascript"></script>
<script src="/Content/kendo/js/kendo.web.min.js" type="text/javascript"></script>
<script src="/Content/kendo/js/kendo.aspnetmvc.min.js"></script>
<!-- Bootstrap 3.3.5 -->
<script src="/Content/bootstrap/js/bootstrap.min.js"></script>
<!-- Morris.js charts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="/Content/plugins/morris/morris.min.js"></script>
<!-- Sparkline -->
<script src="/Content/plugins/sparkline/jquery.sparkline.min.js"></script>
<!-- jvectormap -->
<script src="/Content/plugins/jvectormap/jquery-jvectormap-1.2.2.min.js"></script>
<script src="/Content/plugins/jvectormap/jquery-jvectormap-world-mill-en.js"></script>
<!-- jQuery Knob Chart -->
<script src="/Content/plugins/knob/jquery.knob.js"></script>
<!-- daterangepicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.2/moment.min.js"></script>
<script src="/Content/plugins/daterangepicker/daterangepicker.js"></script>
<!-- datepicker -->
<script src="/Content/plugins/datepicker/bootstrap-datepicker.js"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="/Content/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js"></script>
<!-- Slimscroll -->
<script src="/Content/plugins/slimScroll/jquery.slimscroll.min.js"></script>
<!-- FastClick -->
<script src="/Content/plugins/fastclick/fastclick.min.js"></script>
<!-- AdminLTE App -->
<script src="/Content/dist/js/app.min.js"></script>
<!-- AdminLTE for demo purposes -->
<script src="/Content/dist/js/demo.js"></script>
<!-- Common -->
<script src="/Scripts/common/common.js"></script>
<!-- Render Sections -->
#RenderSection("scripts", required: false)
#RenderSection("HeaderSection", required: false)
This solved my issue. I have added these lines in _layout.cshtml
#*#Scripts.Render("~/bundles/plugins")*#
<script src="/Content/plugins/jQuery/jQuery-2.1.4.min.js"></script>
<!-- jQuery UI 1.11.4 -->
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<!-- Kendo JS -->
<script src="/Content/kendo/js/kendo.all.min.js" type="text/javascript"></script>
<script src="/Content/kendo/js/kendo.web.min.js" type="text/javascript"></script>
<script src="/Content/kendo/js/kendo.aspnetmvc.min.js"></script>
<!-- Bootstrap 3.3.5 -->
<script src="/Content/bootstrap/js/bootstrap.min.js"></script>
<!-- Morris.js charts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="/Content/plugins/morris/morris.min.js"></script>
<!-- Sparkline -->
<script src="/Content/plugins/sparkline/jquery.sparkline.min.js"></script>
<!-- jvectormap -->
<script src="/Content/plugins/jvectormap/jquery-jvectormap-1.2.2.min.js"></script>
<script src="/Content/plugins/jvectormap/jquery-jvectormap-world-mill-en.js"></script>
<!-- jQuery Knob Chart -->
<script src="/Content/plugins/knob/jquery.knob.js"></script>
<!-- daterangepicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.2/moment.min.js"></script>
<script src="/Content/plugins/daterangepicker/daterangepicker.js"></script>
<!-- datepicker -->
<script src="/Content/plugins/datepicker/bootstrap-datepicker.js"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="/Content/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js"></script>
<!-- Slimscroll -->
<script src="/Content/plugins/slimScroll/jquery.slimscroll.min.js"></script>
<!-- FastClick -->
<script src="/Content/plugins/fastclick/fastclick.min.js"></script>
<!-- AdminLTE App -->
<script src="/Content/dist/js/app.min.js"></script>
<!-- AdminLTE for demo purposes -->
<script src="/Content/dist/js/demo.js"></script>
<!-- Common -->
<script src="/Scripts/common/common.js"></script>
<!-- Render Sections -->
i had the same problem . i just convert
#Styles.Render("~/Content/css")
and #Scripts.Render("~/bundles/modernizr") to
#Styles.Render("/Content/css")
#Scripts.Render("/bundles/modernizr")
and its worked.
just dont forget to convert
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
to
#Scripts.Render("/bundles/jquery")
#Scripts.Render("/bundles/bootstrap")
have nice time

Auto-versioning in ASP.NET MVC for CSS / JS Files?

I have read lots of article on how to auto-version your CSS/JS files - but none of these really provide an elegant way to do this in ASP.NET MVC.
This link - How to force browser to reload cached CSS/JS files? - provides a solution for Apache - but I'm a little confused how this could be implemented via ASP.NET MVC ?
Would anyone be able to provide some advice how to do this on IIS7 and ASP.NET MVC - so that CSS/JS files automatically have a version number inserted in the URL without changing the location of the file ?
That is, so links come out link this etc presumably using the URL Rewrite or ?
<link rel="stylesheet" href="/css/structure.1194900443.css" type="text/css" />
<script type="text/javascript" src="/scripts/prototype.1197993206.js"></script>
Thx
When faced with this problem I wrote a series of wrapper functions around the UrlHelper's Content method:
EDIT:
Following the discussions in the comments below I updated this code:
public static class UrlHelperExtensions
{
private readonly static string _version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
private static string GetAssetsRoot()
{
string root = ConfigurationManager.AppSettings["AssetsRoot"];
return root.IsNullOrEmpty() ? "~" : root;
}
public static string Image(this UrlHelper helper, string fileName)
{
return helper.Content(string.Format("{0}/v{2}/assets/img/{1}", GetAssetsRoot(), fileName, _version));
}
public static string Asset(this UrlHelper helper, string fileName)
{
return helper.Content(string.Format("{0}/v{2}/assets/{1}", GetAssetsRoot(), fileName, _version));
}
public static string Stylesheet(this UrlHelper helper, string fileName)
{
return helper.Content(string.Format("{0}/v{2}/assets/css/{1}", GetAssetsRoot(), fileName, _version));
}
public static string Script(this UrlHelper helper, string fileName)
{
return helper.Content(string.Format("{0}/v{2}/assets/js/{1}", GetAssetsRoot(), fileName, _version));
}
}
Using these functions in conjunction with the following rewrite rule should work:
<rewrite>
<rules>
<rule name="Rewrite assets">
<match url="^v(.*?)/assets/(.*?)" />
<action type="Rewrite" url="/assets/{R:2}" />
</rule>
</rules>
</rewrite>
This article discusses how to create rewrite rules on IIS7.
This code uses the version number of the current assembly as a query string parameter on the file path's it emits. When I do an update to the site and the build number increments, so does the querystring parameter on the file, and so the user agent will re-download the file.
UPDATE: The previous version did not work on Azure, I have simplified and corrected below. (Note, for this to work in development mode with IIS Express, you will need to install URL Rewrite 2.0 from Microsoft http://www.iis.net/downloads/microsoft/url-rewrite - it uses the WebPi installer, make sure to close Visual Studio first)
UPDATE: Fixed rule for .min files
I recently spent an entirely fruitless day trying to get automatic bundling (to support auto-versioning) in C# / Net 4.6 / MVC 5 / Razor to work. I read many articles both on StackOverflow and elsewhere, yet I could not find an end-to-end walk through of how to set it up. I also do not care for the way files are version-ed (by appending a query string with version to the static file request - ie. somefile.js?v=1234) because I have been told by some that certain proxy servers ignore query strings when caching static resources.
So after a short trip down the rabbit hole, I've rolled my own version for auto-versioning and included full instructions on how to get it working below.
Full discussion #: Simplified Auto-Versioning of Javascript / CSS in ASP.NET MVC 5 to stop caching issues (works in Azure and Locally) With or Without URL Rewrite
THE PROBLEM: You generally have 2 types of javascript / css files in a project.
1) 3 party libraries (such as jquery or mustache) that very rarely change (and when they do, the version on the file generally changes) - these can be bundled / minified on an "as-needed" basis using WebGrease or JSCompress.com (just include the bundled file/version in your _Layout.cshtml)
2) page specific css/js files that should be refreshed whenever a new build is pushed. (without having the user clear thier cache or do multiple refreshes)
My Solution: Auto-increment the assembly version every time the project is built, and use that number for a routed static file on the specific resources you would like to keep refreshed. (so something.js is included as something.v1234.js with 1234 automatically changing every time the project is built) - I also added some additional functionality to ensure that .min.js files are used in production and regular.js files are used when debugging (I am using WebGrease to automate the minify process) One nice thing about this solution is that it works in local / dev mode as well as production.
How to do it: Auto-increment the assembly version every time the project is built, and use that number for a routed static file on the specific resources you would like to keep refreshed. (so something.js is included as something.v1234.js with 1234 automatically changing every time the project is built) - I also added some additional functionality to ensure that .min.js files are used in production and regular.js files are used when debugging (I am using WebGrease to automate the minify process) One nice thing about this solution is that it works in local / dev mode as well as production. (I am using Visual Studio 2015 / Net 4.6, but I believe this will work in earlier versions as well.
Step 1: Enable auto-increment on the assembly when built
In the AssemblyInfo.cs file (found under the "properties" section of your project change the following lines:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
to
[assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
Step 2: Set up url rewrite in web.config for files with embedded version slugs (see step 3)
In web.config (the main one for the project) add the follwing rules in the system.webServer.
<rewrite>
<rules>
<rule name="static-autoversion">
<match url="^(.*)([.]v[0-9]+)([.](js|css))$" />
<action type="Rewrite" url="{R:1}{R:3}" />
</rule>
<rule name="static-autoversion-min">
<match url="^(.*)([.]v[0-9]+)([.]min[.](js|css))$" />
<action type="Rewrite" url="{R:1}{R:3}" />
</rule>
</rules>
</rewrite>
Step 3: Setup Application Variables to read your current assembly version and create version slugs in your js and css files.
in Global.asax.cs (found in the root of the project) add the following code to protected void Application_Start() (after the Register lines)
// setup application variables to write versions in razor (including .min extension when not debugging)
string addMin = ".min";
if (System.Diagnostics.Debugger.IsAttached) { addMin = ""; } // don't use minified files when executing locally
Application["JSVer"] = "v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace('.','0') + addMin + ".js";
Application["CSSVer"] = "v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace('.', '0') + addMin + ".css";
Step 4: Change src links in Razor views using the application variables we set up in Global.asax.cs
#HttpContext.Current.Application["CSSVer"]
#HttpContext.Current.Application["JSVer"]
For example, in my _Layout.cshtml, in my head section, I have the following block of code for stylesheets:
<!-- Load all stylesheets -->
<link rel='stylesheet' href='https://fontastic.s3.amazonaws.com/8NNKTYdfdJLQS3D4kHqhLT/icons.css' />
<link rel='stylesheet' href='/Content/css/main-small.#HttpContext.Current.Application["CSSVer"]' />
<link rel='stylesheet' media='(min-width: 700px)' href='/Content/css/medium.#HttpContext.Current.Application["CSSVer"]' />
<link rel='stylesheet' media='(min-width: 700px)' href='/Content/css/large.#HttpContext.Current.Application["CSSVer"]' />
#RenderSection("PageCSS", required: false)
A couple things to notice here: 1) there is no extension on the file. 2) there is no .min either. Both of these are handled by the code in Global.asax.cs
Likewise, (also in _Layout.cs) in my javascript section: I have the following code:
<script src="~/Scripts/all3bnd100.min.js" type="text/javascript"></script>
<script src="~/Scripts/ui.#HttpContext.Current.Application["JSVer"]" type="text/javascript"></script>
#RenderSection("scripts", required: false)
The first file is a bundle of all my 3rd party libraries I've created manually with WebGrease. If I add or change any of the files in the bundle (which is rare) then I manually rename the file to all3bnd101.min.js, all3bnd102.min.js, etc... This file does not match the rewrite handler, so will remain cached on the client browser until you manually re-bundle / change the name.
The second file is ui.js (which will be written as ui.v12345123.js or ui.v12345123.min.js depending on if you are running in debug mode or not) This will be handled / rewritten. (you can set a breakpoint in Application_OnBeginRequest of Global.asax.cs to watch it work)
Full discussion on this at: Simplified Auto-Versioning of Javascript / CSS in ASP.NET MVC 5 to stop caching issues (works in Azure and Locally) With or Without URL Rewrite (Including a way to do it WITHOUT URL Rewrite)
I usually append a fake query string to my resource files.. i.e
<link rel="stylesheet" href="/css/structure.css?v=1194900443" type="text/css" />
<script type="text/javascript" src="/scripts/prototype.js?v=1197993206"></script>
It doesn't require any url helpers and works no matter what's running the in background. To be honest I haven't throughly tested this method, but I've found it always fixes any resource caching issues people were experiencing.
You'd probably have to update the v= manually, but it wouldn't be terribly hard to append a version parameter to the resources from a config file somewhere.
Edit:
I went back and throughly read through the content of the link above and realize you've probably already discarded this method. Apologies for suggesting it again.
I suppose next solution with advanced options (debug/release mode, versions):
Js or Css files included by such way:
<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />
Global.JsPostfix and Global.CssPostfix is calculated by the following way in Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
...
string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
JsPostfix = "";
#if !DEBUG
JsPostfix += ".min";
#endif
JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
if (updateEveryAppStart)
{
Random rand = new Random();
JsPosfix += "_" + rand.Next();
}
...
}

Resources