MVC Bundling: How to use parents website bundled resources in child webapp? - c#

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.

Related

Using cached JS Bundle when URL same, but Query String different

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!

Create a Script bundle from multiple locations

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

Scripts in Bundles are rendering with ~/Scripts and so its not found

I have a MVC application that developed partially by someone else. It works in that particular Machine.
When i run the application in my machine, I found the scripts in bundle are rendering with relative path.
<script type="text/javascript" src="~/Scripts/jquery-1.9.1.js"></script>
all the scripts rendering same way where as the CSS files are rendering good. I am using bundles this way.
in BundleConfig.cs
bundles.Add(new ScriptBundle("~/bundles/EMRMobileScript").Include (
"~/MobileQuerys/js/jquery.js",
"~/MobileQuerys/docs/_assets/js/jqm-demos.js"
));
In _Layout.cshtml
#Styles.Render("~/Content/EMRMobilecss")
#System.Web.Optimization.Scripts.Render("~/bundles/EMRMobileScript")
In browser console
GET http://localhost:6326/~/Scripts/jquery-1.9.1.js 404 (Not Found)
Why its rendering with ~ ??
Your client doesn't know what the "~" sign means while the server does. It's just another char the you placed in a string.
When you add a relative src it will be "concated" to the current path, which is
http://localhost:6326/
and thats why you get this request:
http://localhost:6326/~/Scripts/jquery-1.9.1.js
Now, when you use this line of code in the server:
#Styles.Render("~/Content/EMRMobilecss")
The server knows how to map the path(where ~ is the applications "root" directory).
In order to render the specifc bundle from your js, use this:
<script src='http://localhost:6326/Scripts/jquery-1.9.1.js'></script>
If it's running on the local host, it's a known bug that occurs. If IIS is running the application it more than likely is an issue with that. Have you tried just running this with just the MVC?
Otherwise there isn't really a work around for this when running it on IIS.

Is there a way to have IIS default document (default.asp) take precedence over .Net routing?

I am trying to migrate a site from classic ASP to .Net (using WebMatrix). I am planning on mixing the new .cshmtl pages in with the legacy .asp files and slowly migrate the site over time. To date, I have individual .asp pages for each piece of content (each url). As I move to .Net, the content will be coming from a database, so there will not be specific pages for each url. I love the power of routing that is available in .Net and it works well and plays nicely with the classic ASP with one exception so far.
My example is that I have an ASP page located at /crafts/default.asp and currently reference that everywhere as just /crafts/. The IIS default document setting takes care of serving the default.asp page when i just reference /crafts/. I want to start to post new items into this area of the site but run as .Net pages. so I created a /crafts.cshtml page which is designed to handle different urls and go to the database and lookup the item and display the info. So as an example I have legacy page at /crafts/fall-crafts.asp and this works fine - because there is an .asp page sitting there named that. But when i reference /crafts/ .Net takes over and serves the /crafts.cshtml page instead of serving the /crafts/default.asp file. Is there something I can do to keep the power of .Net routing in place, but still have IIS serve the default document in this case?
Modify your RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("crafts/");
...
See RouteCollection.Ignore for documentation.
Similar question (mentions some other possibly needed workarounds): Ignore folder in ASP .NET MVC
The default "routing" built into the Web Pages framework relies on matching incoming URLs to physical files on disk, looking first for the existence of cshtml or vbhtml files. All the time you have a crafts.cshtml file at the same level as a folder called crafts, the file will be served first. The solution is to remove or rename you crafts.cshtml file, but currently that will mess up your URLs.
There is a package which offers greater management over routing in Web Pages applications. It's called Routing For WebPages. It allows you to route URLs to any file name you like. You can download that via the Package Manager or Nuget and read this article that I put together that explains how to use it: http://www.mikesdotnetting.com/Article/187/More-Flexible-Routing-For-ASP.NET-Web-Pages.

How to create new sub domain website using admin panel of existing site

I am developing a site in asp.net MVC, From which a admin can create a sub domain of that site with different css and images but same DataBase.
For eg: Suppose I have developed a site www.xyz.com, and admin of xyz.com want to create a sub domain like http://sub.xyz.com/
So when admin put a name of sub domain, Then application create a new folder and all the code of main site copy to that folder.
So, Because asp.net is not a open source code then could any one tell me the answer of some points.
How to manage assembly reference between main site and sub domain.
How to register sub domain during run time.
Procedure of change css and images after publish a project.
Perhaps we can simplify the problem a little if you don't need your applications to run in seperate application pools.
Create a wildcard mapping on your DNS server for all subdomains of xyz.com to point to the same host.
Configure your web server to route all hostnames to the same website.
Instead of copying the whole app to a new folder for each subdomain, modify the original app to behave differently depending on the host url used to call it. The modified behaviour might be as simple as changing the css & images folder depending on the url subdomain. For example your master page could contain lines like this:
<link type="text/css" rel="stylesheet" href="css/<%= Request.Url.Host %>/default.css" />
<img src="images/<%= Request.Url.Host %>/header-logo.gif" />

Categories

Resources