Let's assume our app is offline, i.e. we can't use 3rd party CDNs thus we're creating our own.
I'd like to host all of the vendor scripts in a separate (Parent) web app and then include them in the bundles in several other MVC Apps.
e.g.
http://localhost/parentWeb/Scripts/jquery.js
http://localhost/parentWeb/Scripts/jquery-ui.js
http://localhost/parentWeb/Scripts/globalize.js
I'd like to include in the ASP.NET MVC App Website located in: http://localhost/parentWeb/childWeb
i.e. do something like this:
bundles.UseCdn = true;
bundles.Add(
new ScriptBundle(
"~/bundles/VendorScripts",
"http://localhost/parentWeb/Scripts/jquery.js",
"http://localhost/parentWeb/Scripts/jquery-ui.js",
"http://localhost/parentWeb/Scripts/globalize.js"));
...which of course isn't currently possible. Is there a good workaround?
You can't bundle external resources. If you think about it, it makes sense why you can't. It would require the bundler to actually download the resource and save it to the filesystem before it could work with it, and of course do it all asynchronously with some sort of fallback if the external resource couldn't be reached. And, then, it would have to do this on every page load because it can't check for lastmod (and therefore, know whether it actually needs to rebundle or not) without fetching the resource first.
If you use a CDN resource, the bundler merely prints the URL directly to the page; it doesn't make any modifications. Even then, it only lets you create a "bundle" of just that one URL, because 1) it wouldn't make sense to bundle multiple CDN resources since that would defeat the purpose of a CDN and 2) the bundle only exists in this scenario to provide a fallback if the CDN resource is unavailable. Otherwise, you would be served just as well by just hardcoding it to the page and not worrying about setting up a bundle at all.
I know this is an old topic, but I came here looking for an actual way to bundle CDN resources. From #Chris Pratt's answer, I understood it wasn't possible.
If you're wondering, I am working on optimizing an an existing project according to Google's Web Performance Best Practises which gives a low score when there are multiple script tags and a higher one when all scripts are bundled into a single script reference.
I needed a way to bundle all the CDN script resources as well as local resources in order. I worked on this github repo, which solved my problem.
With it, you build a bundle with a list of bundles, each containing a reference to the cdn resource, local resource to save to, and a Boolean indicating whether or not you want the bundle to be minified.
List<Bundle> jsBundles = new List<Bundle>();
jsBundles.Add(new Bundle("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js", #"~/jquery.min.js", Bundle.BundleType.JavaScript, false));
jsBundles.Add(new Bundle("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js", #"~/jquery-ui.min.js", Bundle.BundleType.JavaScript, false));
jsBundles.Add(new Bundle(#"~/my-local-script.js", Bundle.BundleType.JavaScript, true));
To place on the page, you use
#jsBundles.Load();
This will process all bundles in the list, downloading content for bundles that have not been downloaded in the last 24 hours (It updates every 24 hours or when the web application restarts). All content downloaded will be placed in local files (where specified).
All content will be combined into the final result which will be spooled into the page in a script tag (or link tag for CSS).
The Load function also accepts a local File URL for the final script/css content. If specified, a tag with a src to the relative path for that local file will be given instead. E.g.
#jsBundles.Load("~/js/all-my-scripts.js");
The above statement will return something like:
<script src="~/js/all-my-scripts.js"></script>
An async attribute may be added to the script tag if the second parameter of the Load function is provided.
It also works on css cdn resources too. E.g.
List<Bundle> cssBundles = new List<Bundle>();
cssBundles.Add(new Bundle("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css", #"~/jquery.ui.css", Bundle.BundleType.CSS, false));
cssBundles.Add(new Bundle(#"~/css/my-local-style.css", Bundle.BundleType.CSS, true));
#cssBundles.Load("~/css/all-my-styles.css");
This is for the benefit of those, who like me, came in here looking for a way to actually bundle CDN resources.
I have found a solution, which has nothing to do with CDN. Basically, granted the childWeb is hosted in the parentWeb's subdirectory, the following bundle configuration in the childWeb apps picks the file from the parentWeb and bundles them as usual:
bundles.Add(
new ScriptBundle(
"~/bundles/VendorScripts").Include(
"~/../Scripts/jquery.js",
"~/../Scripts/Scripts/jquery-ui.js",
"~/../Scripts/globalize.js"));
the important bit being: ~/../, which takes you one level up from the root location.
To use ScriptBundles with CDN resources, you need to use the overloaded constructor. Unfortunately you need to specify multiple ScriptBundles per file.
Here's a great blog post explaining things:
http://www.hanselman.com/blog/CDNsFailButYourScriptsDontHaveToFallbackFromCDNToLocalJQuery.aspx
And here's a code snippet:
bundles.UseCdn = true;
var bundle = new ScriptBundle("~/bundles/bundleNameHere", "//cdn.host.path/to/file");
// Path to the version of the file on your server (in case the CDN fails)
bundle.Include("~/../Scripts/path/to/file");
// JS expression to run, to test if CDN delivered the file or not
bundle.CdnFallbackExpression = "window.ValueYouExpectToBeTruthy";
bundles.Add(bundle);
The purpose of bundle is reduce the traffic of your web server where your web application hosted. using bundle the script files will not load in parallel. this is needed when you are using your local files.
but in case of cdn the files will load from cdn server so you don't have need to make bundle for cdn. you need bundle for local files only
Related
I have a website in IIS, under the website there are couples of web applications (website and web applications all are mvc web application).
The website uses ASP.NET MVC bundling -
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new StyleBundle("~/Content/css")
.IncludeDirectory("~/Content/Styles", "*.css", true));
bundles.Add(new ScriptBundle("~/Content/js")
.IncludeDirectory("~/Content/Scripts", "*.js", true));
}
In website _Layout.cshtml -
#Styles.Render("~/Content/css")
#Script.Render("~/Content/js")
When website is browsed (e.g. mywebsite.com), I can see querystring attached to those references.
<link href="/Content/css?v=asdsf$dfsdfdslkko" rel="stylesheet" />
<script src="/Content/js?v=jjkase$rads#asdasd"></script>
And for my child web application under that website in IIS, if I have to use same bundled resources, I can do (in webapp layout page) -
#Styles.Render("/Content/css")
#Script.Reder("/Content/js")
When webapp is browsed (e.g. mywebsite.com/mywebapp), I can access those resource from parent website but I couldnot get those querystrings in style and script references.
<link href="/Content/css" rel="stylesheet" />
<script src="/Content/js></scripts>
The only difference is I must use starting / for webapp which is the cause for not generating querystring. It's a weird behavior. I need querystring while browsing webapp too (to avoid cache). If I used / instead of ~ in my parent website also, querystring gets dissapeared (DISGUSTING).
Is there anyway to retain querystring even / is used for referencing bundles? Any help will be highly appreciated.
Thanks in advance.
First off, bundling is project-centric. There's no way to share bundles in one application with another, child or not. What you're doing here is essentially taking advantage of the fact that the bundling framework actually caches the created bundles to a real file, which then allows you to reference it in the other project as you would any other random static JavaScript/CSS file. However, with this approach, you're going to lose some of the niceties of using a bundle directly, such as the automatic appending of that cache-busting query string.
Technically, you can append your own query string. There's nothing special about it, really; it's just a random string that tricks the user's browser into thinking it's a different resource, and thus making it fetch it again. For example:
<script src="/Content/js?foo123></scripts>
However, you're going to be responsible for updating the query string whenever you change any of the scripts in that bundle. Using the actual bundle directly, it's smart enough to know something has changed and append a different query string, but if you're just statically linking, then there's no automated process to do that for you.
My site does not seem to use the cached JS/CSS files when the query string parameter changes.
When the user navigates the site, they go through a sequence of "pages"
www.myurl.com/Controller/Action/Id?Indx=1
www.myurl.com/Controller/Action/Id?Indx=3
www.myurl.com/Controller/Action/Id?Indx=4
www.myurl.com/Controller/Action/Id?Indx=2
www.myurl.com/Controller/Action/Id?Indx=6
Between each transition, there is a post to the Controller-Action.
I would like to be able to use the cached version of the LESS / CSS / JS files when the user hits the page after the first time (its always the same files).
However, it seems - because the Query string parameter (Indx) is different - the entire bundle is called from the server again & the CDN is also hit again.
This seems counter-productive. Is there a way out of this mess?
I am sending over my JS / LESS files as a minified bundle or I am using a CDN (for JQuery etc). For the minified bundles, in the cshtml file, I calling them using "Styles.Render"
Any insight would be great! Thanks!
When the bundle is registered in MVC4, what is responsible for "intercepting" incoming http requests for /bundles/someBundle?v=1hDzBpmYJm4Iu-OjRN1YqS1WeNThVl0kStLJGP8WCr41?
also since hash for each bundle is calculated only once (at a first request), where is that actually held, - and is it possible to return 404 if an incoming hash does not match
what is responsible for "intercepting" incoming http requests for ~/bundles/someBundle
There are no incoming requests to ~/bundles/someBundle. It's the server side helper that you are using (Scripts.Render) that on the server (within the same HTTP requests) interprets this value and spits the correct url in the resulting HTML.
also since hash for each bundle is calculated only once (at a first request), where is that actually held,
The actual bundle contents is stored in the server side cache : HttpContext.Cache. The actual hash represents a SHA256 hash on this content that is calculated every time you use the Scripts.Render helper.
UPDATE:
It's the System.Web.Optimization.BundleModule that is auto-registered when you reference the System.Web.Optimization assembly that is responsible for intercepting requests to urls like /bundles/someBundle?v=1hDzBpmYJm4Iu-OjRN1YqS1WeNThVl0kStLJGP8WCr41 and returning the actual contents.
You should have a file called BundleConfig.cs in a folder App_Start in your web project.
That section basically links a url "/bundles/something" to some script(s). When in accessing the site in Release mode (not debug activated), it will automatically merge the script into one in-memory file, minimize the script, add caching headers to the request and generate a hash of the file content.
If you are in debug, all the scripts should be separated to make debugging easier.
You either redefine the bundles you see in that file or declare some of your own.
Enjoy.
The reason to append a query string with a parameter based on contents of actual files that you are serving is solving the caching problem. Sou you can inform browsers to cache this requests for a long period of time and speed up subsequent pages load times.
So for developers of this bundling mechanism there is no difference what that parameter is. Only thing that is important is that if you change contents of your scripts or css - hash would change and it will force clients browser to request new files from the server.
As for what is responsible for intersepting this requests - there is source code of MVC available on codeplex, but i guess it plugs right into routing.
In my project i will be having an link like
Download
I want the users to download files of different types. The file will be in the root folder. When i am clicking on the link it is displaying an error. This is the plugin to install in the chrome. If the user download this link and open then it will automatically add to the chrome.
How can i do this.
The file is not even downloading.
This isn't a valid path:
~/hello world.crx
The ~ character is for use server-side to denote the root of the application. Client-side it has no meaning. The browser doesn't know what the root of the application is (or what the application is at all), it's just sending requests to resources at addresses. And it doesn't know what to do with that address.
You'll need to either use some server-side logic to translate that path into a browser-useable path, or manually make it a relative or absolute path.
If the ASP.NET MVC Framework isn't translating this for you then you're probably using a version that requires a little more manual work for it. Try something like:
Download
(Note: This assumes the use of the Razor view engine. If you're not using that then you'll want to use whatever your view engine equivalent is.)
What you need to do is set up a directory online, where you can host the file.
I also see that in your aref you don't want to type the full path so denote it with a /hello_world.crx, but make sure that you've set up a base href:
<base href="http://yourdomain.com/something/">
Try renaming the file to remove any spaces e.g. "hello_world.crx" and then change the name in the link code to match.
if a webpage and the downloadable file is in the same location
(i.e)
SampleFolder->Download.html
SampleFolder->hello world.crx
then try the below
download
If the webpage and the downloadable file in different location
(i.e)
SampleFolder->Download.html
SampleFolder->Downloads->hello world.crx
then try the below
download
How do I reference a file outside my web site's root directory?
For example my website is located at C:\dev\TestSite
I am using ASP.NET with XSP. The webapp will be deployed on Apache using mod_mono.
I have images in C:\images and I would like to do this:
<img src="C:\images\logo.gif"/>
Your img tag's src value is going to be sent to the client. You need to specify those paths relative to your document root. Your best bet is to set up a virtual folder (in IIS, alias is the apache equivalent) to point to the c:\images path and then change the mentioned tag src path as follows
<img src="/images/logo.gif" />
To do this in apache, you need an alias in your httpd.conf. The line looks like this
Alias /images c:/images
Here's the docs http://httpd.apache.org/docs/2.0/mod/mod_alias.html#alias
While it might be possible to do this through some hacky method, it's not a good idea. Allowing the IIS account to access files/folders on the greater file system would be a big potential security hole.
The best way to accomplish this is to use IIS Virtual Directories. Put your content in folders dedicated to supporting the site, DO NOT make your entire C: drive a virtual directory.
I'm going to assume that C:\images\logo.gif is the path on the server, and not the path on the client.
The src attribute is interpreted by the html client (i.e. Internet Explorer). The client can't see anything outside of your web directory. In fact, the client can only see things inside your web directory if you've provided permission for them to do so. Thus, this isn't an ASP.NET issue, but an issue of how web clients have access to web servers... which is designed this way for security.
In order for your application to use these images, you've got a couple of options that immediately spring to mind - neither of which is ideal:
The ASP.NET code (in the codebehind) is going to need to go and grab the file, and serve it out in the html stream that is being served to the client, which is more a complex task than I suspect you are willing to embark on.
The ASP.NET code (using System.IO) is going to need to go and grab the file from it's home location in C:\images\logo.gif and copy it to a location that is accessible to the client - you could create a temporary directory, copy your image to it, serve it out, delete it, delete the directory.
Both of these are certainly hacks that should be avoided if possible, but if you're adamant that this is what you want to do, this will allow you to do it via your ASP.NET app.
The most ideal solution is to add C:\Images as a virtual directory to your document root, i.e. /ImageCentral - this way you can have images that are central to multiple websites stored in this directory, it can then be referenced by clients for any of the websites just by adding virtual directories to each of them pointing at the central images folder. As DaveSwersky points out, don't make any directories containing sensitive information virtual directories, the minute you add a virtual directory to an externally visible website, you're giving people free reign to any of the information in it.
Good luck
You can't do this from within the HTML code of your page. The HTML page can only reference web accessible content (in regards to images, CSS, javascript, etc). You could create a virtual directory that points to your images folder so that it becomes web accessible.
EDIT:
The apache way inside of your conf file.
Alias /images "C:/Images"
And a little walkthrough from some dude.