MVC4 Bundling: where the URL for bundle is held - c#

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.

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

HTTPHandler does not handle secondary requests

I want to run my personal web sites via an httphandler (I have a web server and static ip at home.)
Eventually, I will incorporate a data access layer and domain router into the handler, but for now, I am just trying to use it to return static web content.
I have the handler mapped to all verbs and paths with no access restrictions in IIS 7 on Windows 7.
I have added a little file logging at the beginning of process request. As it is the first thing in the handler, I use the logging to tell me when the handler is hit.
At the moment, the handler just returns a single web page that I have already written.
The handler itself is mostly just this:
using (FileStream fs = new FileStream(Request.PhysicalApplicationPath + "index.htm",
FileMode.Open))
{
fs.CopyTo(Response.OutputStream);
}
I understand that this won't work for anything but the one file.
So my issue is this: the HTML file has links to some images in it. I would expect that the browser would come back to the server to get those images as new requests. I would expect those requests to fail (because they'd be mapped to index.htm). But I would expect to see the logging hit at least twice (and potentially hit recursively). However, I only see a single request. The web page comes up and the images are 'X's.
When I refresh the browser, I see another request come through, but only for the root page again. The page is basic HTML, I do not have an asp.net application (nor do I want one, I like HTML/CSS/JS).
What do I have to do to get more than just the first request sent from the browser? I assume I'm just totally off the mark because I wrote an HTTP Module first, but strangely got the same exact behavior. I'm thinking I need to specify some response headers, but don't see that in any example.

Server side redirect truncating request payloads

I'm on IIS 6 and I have an ASP.Net 4.0 site that's a single page to serve as a SOAP reverse proxy. I have to modify the return content in order to delete a trouble node from the response and add a tracking node.
In order to facilitate its function as a reverse proxy for all addresses, I have the 404 on the server set to a custom "URL" of "/default.aspx" (the page for my app)
For requests without a payload, it works perfectly - such as for ?WSDL Urls. It requests the proper URL from the target system, gets the response and sends it back - it's pretty utterly transparent in this regard.
However, when a SOAP request is being made with an input payload, the Request.InputStream in the code is always empty. Empty - with one exception - using SOAPUI, I can override the end point and send the request directly to /default.aspx and it will receive the input payload. Thus, I have determined that the custom 404 handler is - when server-side transferring the request - stripping the payload. I know the payload is being sent - I have even wiresharked it on the server to be sure. But then when I add code to log the contents of Request.InputStream it's blank - even though Request.ContentLength shows the right content length for the original request.
I've also been looking for a good way to use ASP.Net to intercept the requests directly rather than allowing the normal IIS 404 handler to take care of it but even with a wildcard mapping, I can't seem to get the settings right nor am I fully confident that it would help. (But I'm hoping it would?)
Finally, I don't have corporate permission to install MVC framework.
Thus, I need either some configuration for IIS I am missing to make this work properly or some other method of ensuring that I get the request payload to my web page.
Thanks!
What about using an HTTP Handler mapped to all requests?
You'll need to add a wildcard application mapping as detailed here and correctly configure your HTTP Handler.

Running ASP.NET MVC application behind a proxy with different root relative path

I'm having trouble with paths in a ASP.NET MVC application that's running behind a proxy.
Our IIS Application root path is for example http://server/MyApp/
meaning that all urls using the application root ("~/",Url.Action("MyAction","MyController")) are resolved to "/MyApp"
Now we're running behind a proxy server that forwards all requests, but requires you to access the application through a URL like this:
"/Secury/Proxy/RubbishUrl/MyApp"
Because the proxy url is only available on the client, I thought of creating a cookie with the path prefix, and insert this before each generated URL on the server.
Now the question is, what's the best location in code to modify each URL that's resolved/sent to the client (to resources, controller actions, images etc)?
Every path in the application is resolved with the MVC methods (Url.Content, Url.Action etc).
Update:
Not actively looking for an answer anymore (though still interested in a proper solution)
Most of the time Proxies do their own URL translation, however in this case the proxy server is ignoring paths that are transfered in JSON, and they are processed.
My 'solution' for now is just not passing paths in JSON, but instead:
using proper ID's and values in the JSON requests
creating template in URL's in the HTML (which are resolved properly),
replace the ID's and values in the URL template with the values from the JSON requests
This method is actually a much 'cleaner' way IMO then passing the URL's.
You can create your own asp.net mvc controller factory where to decide which controller and action will serve the response based on the requested url. Check this url for a good blog post on how to do this - http://nayyeri.net/custom-controller-factory-in-asp-net-mvc.

Categories

Resources