In this article, I'll show you how to build a site so that it works in Debug or Release mode using the StyleBundles and ScriptBundles. I'll demonstrate these techniques with this website template.
The Virtual Path Matters!
Apart from what you've probably heard and read in various Forums or Stackoverflow posts regarding the way to work with the StyleBundle and ScriptBundle classes, the "virtualpath" parameter of the classes DOES matter, as far as how it is named. Some answerers have gotten this right.
Let me clarify this, if you have ALL of your CSS files in the root of the Content folder, then no, it doesn't matter. It only matters if you have various folders inside the Content folder that can contain many different sets of styles for various reasons, and you want to maintain that folder structure.
I'll show you how to keep those folders without having to drop everything down to the root.
What you need to do is match the bundle name with the actual virtual path to your files!
I'll explain this in a moment, but let me show you what the framework does with the bundles from Debug mode to Release mode.
Let's say I have a Content folder with my CSS files and images in a structure like this:
This is how I want to keep my files so they are separate from each other.
So let's say I've installed the halcyonic HTML into the _Layout.cshtml page. And I've then created a StyleBundle like this:
bundles.Add(
new
StyleBundle(
"~/Content/halcyonic"
).Include(
"~/Content/halcyonic/css/style.css"
,
"~/Content/halcyonic/css/5grid/responsive.css"
));
... this will work fine in Debug mode and return a path as:
http://localhost:32290/Content/halcyonic/css/images/someimage.png
But if I were to run this in Release mode, by changing the "debug" attribute on the compilation element in the web.config file to false...
<
compilation
debug
=
"false"
targetFramework
=
"4.5"
/>
... and then run the application, I would get dreaded 403 Forbidden error and my page layout would break. And you can clearly see below that my page doesn't have any styles attached.
And this is what the path to the same image file would look like in Release mode:
http://localhost:32290/Content/images/someimage.png
Obviously we can see that the path changed when changing to Release mode, but what happened? I fuddled over this for a long time and was determined to figure it out and finally found a pattern.
If I changed the parameter name of the bundle to something that more closely matched to the actual path to the files, it started working. So if you look at the picture of the Content folder and it's file structure again, you can see that the CSS file, style.css, lives in the /Content/halcyonic/css folder.
So I tried:
bundles.Add(
new
StyleBundle(
"~/Content/halcyonic/css"
).Include(
"~/Content/halcyonic/css/style.css"
,
"~/Content/halcyonic/css/5grid/responsive.css"
));
Of course, each time I made a change here I had to update the Render method in the _Layout.cshtml page.
@Styles.Render("~/Content/halcyonic/css")
... and this too didn't work. I got the same 403 Forbidden error.
So then I found out that the last part of the virtualPath is essentially a throw away name. It doesn't really mean anything or have any other impact other than telling the framework that the information before it is important. I'll tell you why in a second.
So then I tried this:
bundles.Add(
new
StyleBundle(
"~/Content/halcyonic/css/styles"
).Include(
"~/Content/halcyonic/css/style.css"
,
"~/Content/halcyonic/css/5grid/responsive.css"
));
@Styles.Render("~/Content/halcyonic/css/styles")
... and it worked!
Why does this work?
Let's take a closer look at why this now works.
bundles.Add(
new
StyleBundle(
"~/Content/halcyonic/css/styles"
).Include(
"~/Content/halcyonic/css/style.css"
,
"~/Content/halcyonic/css/5grid/responsive.css"
));
I've lined up the StyleBundle parameter with the included files paths to show you how they correlate with each other. Notice that they all match up to the last slash before the style sheet. This is the important thing to remember. Make the bundle parameter match the path up to the location of the style sheet, and then add a name, any name after that.
This will work:
bundles.Add(
new
StyleBundle(
"~/Content/halcyonic/css/foo"
).Include(
"~/Content/halcyonic/css/style.css"
,
"~/Content/halcyonic/css/5grid/responsive.css"
));
@Styles.Render("~/Content/halcyonic/css/foo")
It doesn't matter what the trailing name is. The framework will ignore it, but you need it to tell the framework that everything before it will be used as the virtualPath in Release mode. If /css was the trailing name, then it would say that it will look for the files in /Content/halcyonic, which is wrong.
And before you say, "Well why can't I put just a trailing slash after css, as in /Content/halcyonic/css/?" (notice the trailing slash). That also won't work. While the CSS will render Ok, some images may break, so put in a name with no trailing slash.
So end the virtualPath parameter with a name without a trailing slash.
Using this method will also allow you to use the relative image paths in the CSS file. One thing I've found that always works the best is to have the images folder as a child underneath the style sheet.
For KendoUI Users
To allow your KendoUI widgets to work in Release mode and Debug mode, here are the bundles and Render methods:
bundles.Add(
new
ScriptBundle(
"~/bundles/kendo/2012.3.1401/kendoscripts"
).Include(
"~/Scripts/kendo/2012.3.1401/kendo.web.*"
,
// or kendo.all.*
"~/Scripts/kendo/2012.3.1401/kendo.aspnetmvc.*"
));
// The Kendo CSS bundle
bundles.Add(
new
StyleBundle(
"~/Content/kendo/2012.3.1401/kendostyles"
).Include(
"~/Content/kendo/2012.3.1401/kendo.common.*"
,
"~/Content/kendo/2012.3.1401/kendo.default.*"
));
@Styles.Render("~/Content/kendo/2012.3.1401/kendostyles")
@Scripts.Render("~/bundles/jquery","~/bundles/kendo/2012.3.1401/kendoscripts")
You would just change the version folder name to yours.
Conclusion
So that's essentially it! And just to be clear, this same procedure would be used for the ScriptBundle classes as well.
I know there's a lot here, but hopefully it's pretty straight forward and understandable. This should prevent you from having any more issues with getting the the CSS and Scripts to render properly in Release mode.
Have fun!
Hi kahanu,
it's early in the morning and this article "made my day"! Thanks for sharing!
Daniel
Hey King Wilder,
You're the same guy that provided the awesome SecurityGuard... https://github.com/kahanu/Security-Guard
While searching for 403 forbidden related to style bundling I found you once again.
Hooray... now I got it forever and ever!
Using this method will also allow you to use the relative image paths in the CSS file.
This is the biggest win.
Thank you very much for the detailed post.
Best,
Leniel
http://leniel.net
Awesome!
I've spend days figuring out why it didn't work in release mode...
Thank you sooo much ;)
Others and I are very greatful for this post! I couldn't thank you enough.
On a side note, do you have any idea why .min files are having issues? specifically KendoUI.min files?
Thanks for any update!
Thank you very much dude! My problem was solved... Still I want the official explanation from microsoft for this behaviour.. From my opinion this is an issue.
Thanks again!
Thank you, kahanu
its solved my problem
Thanks for the posting. It solved my issue.
However, in your conclusion, you said this also applies to ScriptBundle. In your example,
bundles.Add(
new
ScriptBundle(
"~/bundles/kendo/2012.3.1401/kendoscripts"
).Include(
"~/Scripts/kendo/2012.3.1401/kendo.web.*"
,
// or kendo.all.*
"~/Scripts/kendo/2012.3.1401/kendo.aspnetmvc.*"
));
The virtual path in ScriptBundle "~/bundles/kendo/2012.3.1401/kendoscripts" does not match the virtual path in Include method.
Is there anything I undertand wrong?
Thanks,
Jason
Thank you very much, I spent whole day to try to fix it. Thank you.
I will keep your blog on top.
Best regard
TPL
@Jason Ge - I'm going to update my article, but for scripts it doesn't need to work the same way. For example, for Kendo script bundles you can simple be created like this:
bundles.Add(new ScriptBundle("~/bundles/kendo").Include(
"~/Scripts/kendo/2014.1.321/jquery.min.js",
"~/Scripts/kendo/2014.1.321/kendo.all.min.js"));
I have this working in many projects. Give it a try.
I'll think you'll find the problem was that IIS was attempting to serve a physical path and not the bundle.
This is why you get a 403 (you don't have permission to access the path).
So as long as your bundle name doesn't match an actual path on the server you should be fine.
This is very usefull for us
Hi Kahanu,
Thanks a lot for this very helpful article.
You save my day. I ran into the same issue after deploying our ASP.NET app to IIS.
thanks alot, It helped
@rahul - this article is only for bundling CSS style sheets and javascript files, not images.
If you are talking about the images referenced in a CSS file, then you have to make sure the path to those images is correct in the CSS file, and then it will work. As a rule, I usually move the /images folder inside the folder my CSS file is in, this way the path to the CSS images can be referenced like:
background-image: url(images/myimage.jpg);
Instead of trying some path like:
background-image: url(../../images/myimage.jpg);
I hope that answers your question.
Thanks!
muchas gracias ; )
Hi!
This article saved my day (and my job) for the second time! Thanks a lot!
regards,
enzo
Lifesaver article! Really good, helped a lot! Thanks for it!
:)
I deployed a new project to our live server and bang - 403 error. I figured out it was to do with Kendo but had no idea how to fix it. A quick Google resulted in this article, and I am now live.
Thanks a lot dude. You truly are a King.
Good Afternoon sir.
This article really helped me out.
Thanks. :)
Found this article after 2 hours of banging my head against the wall. Hit 'refresh' and said outloud "there's no way this is gonna work" ... as my perfectly styled webpage opened in front of me!
@owenlt - I'm sorry you have some drywall to fix, but hopefully this article will save more unsuspecting walls. :^)
Hi kahanu,
Really very good article
Thanks for sharing!
Regards,
Sriram.
Gracias, me funcionó perfectamente!!!!.
Gracias, me funciono Perfectamente !!!.
A mi me pasaba Que CUANDO ejecutaba la aplicacion directly from depuración en Visual Studio, los Estilos me cargaban Y Todo funcionaba bien, Pero CUANDO desplegaba en Servidor en el IIS Los Estilos ya no cargaban y salia de error 403 lo prohibido. Se solucionó agregando es es BlundleConfig.cs en La Línea:
bundles.Add (nueva StyleBundle ( "~ / contenido / css") ----> se le adicionó CUALQUIER Numero yo escogí "Estilos" y quedo Así:
bundles.Add (nueva StyleBundle ( "~ / contenido / CSS / Estilos")
Y Por Último en el diseño, en:
@ Styles.Render ( "~ / contenido / css") ---> tambien adicioné la Palabra "Estilos" y quedo asi:
@ Styles.Render ( "~ / contenido / CSS / Estilos")
Y de correoproblema se solucionó mi problema.
Hi kahanu,
thanks for your post. I came across your site struggling with this issue while deploying my App to Windows Azure in Release mode. I also use Kendo UI and the images were missing.
Thanks very much for your help.
Regards
Sven
Thanks for this useful information !
Timely help and saved my RnD time :)