We have a site served on a web farm. The farm is behind an SSL Accellerator which handles the encryption. This means that our IIS servers see all incoming connections as http, even though users all connect to the site via https.
We are beginning to use the EnableCDN=true property of ScriptManager. While on our dev environments where there is no SSL Accellerator the references to the js files on the CDN are rendered with https, on the production environment they are rendered insecurely over http which is causing the js to be blocked with "Only secure content is displayed" errors.
Short of manually updating all the script references in scriptmanager or re-writing the HTML on the way out via a module, does anyone know of a way to force the scriptmanager to render its references via https?
EDIT:
After doing some reflector review, I don't believe this is possible. I've put the following hack into place, however this is obviously fragile as it involves accessing a private field. If anyone can see a better way I would love to hear it.
var secureConnectionField = ScriptManager.GetType().GetField("_isSecureConnection", BindingFlags.Instance | BindingFlags.NonPublic);
if (secureConnectionField != null)
secureConnectionField.SetValue(ScriptManager, true);
If you use ASP.NET 4.0 or higher then one of the solution is to use ScriptResourceMapping feature of the ScriptManager control.
For example in global asax you can add the following code:
void Application_Start(object sender, EventArgs e) {
// map a simple name to a path
ScriptManager.ScriptResourceMapping.AddDefinition("jQuery", new ScriptResourceDefinition {
Path = "~/scripts/jquery-1.3.2.min.js",
DebugPath = "~/scripts/jquery-1.3.2.js",
CdnPath = "http://ajax.microsoft.com/ajax/jQuery/jquery-1.3.2.min.js",
CdnDebugPath = "http://ajax.microsoft.com/ajax/jQuery/jquery-1.3.2.js"
});
}
So, as you can see you can set CDN paths explicitly. Also, you can override the script mapping for standard Ajax files.
More information can be found in this article: http://weblogs.asp.net/infinitiesloop/archive/2009/11/23/asp-net-4-0-scriptmanager-improvements.aspx
To handle a situation similar to yours, I configured the BundleCollection to use the CDN and two different version of the library for debug and production.
The result of these settings is that the non minified one will be used while debugging and the minified one from CDN will be used in production. The local minified one is ignored.
[...]
bundles.UseCdn = true;
[...]
var jQueryBundle = new ScriptBundle("~/bundles/jquery");
jQueryBundle.CdnPath = "//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js";
jQueryBundle.Include("~/scripts/jquery-1.9.1.js");
jQueryBundle.Include("~/scripts/jquery-1.9.1.min.js");
bundles.Add(jQueryBundle);
Note that I have not specified the protocol in the CdnPath, the client's browser will automatically use the current protocol the client is connected from, http or https.
The client's browser will receive the following tag in production:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
The developer's browser will receive the following tag:
<script src="/Scripts/jquery-1.9.1.js"></script>
Both will use the correct protocol on the browser.
Normally your development and production servers will have different IP ranges. Ask the developement team to apply a "protocol switch" based on the IP addresses in the framework dlls.
Wether you are using #Razor it ASPX, I assume you have a Layout/MasterPage somewhere.
CDNs' true power lies in specifically having the script remotely hosted from some other source than your server's. Doing so prompts to be more likely that the user's browser has picked it up from a different location. CDNs perform best for core libraries, such as AngularJS or jQuery. The less popular the script you are importing is, the less recommended is to serve it through CDN.
I find it more suitable to hardcode-it on your Layout. If you use just the // instead specifying the protocol with http:// or https:// it should pick up the same protocol the page is being called.
<script type="text/javascript" src="//{yourCDNsource}"></script>
If you locally compress/minify a remote CDN it defeats the purpose. Better use NuGet or Bower for those.
Check Google CDNs
Use this global.asax
void Application_Start(object sender, EventArgs e) {
// map a simple name to a path
ScriptManager.ScriptResourceMapping.AddDefinition("jQuery", new ScriptResourceDefinition {
Path = "~/scripts/jquery-1.3.2.min.js",
DebugPath = "~/scripts/jquery-1.3.2.js",
CdnPath = "http://ajax.microsoft.com/ajax/jQuery/jquery-1.3.2.min.js",
CdnDebugPath = "http://ajax.microsoft.com/ajax/jQuery/jquery-1.3.2.js"
});
}
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.
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
i am trying to compress the webpages of my website to increase its speed.
i am done with JS and CSS compression.
now i want to compress my aspx pages before the response is out.
i am using this code in global.asax file of my website
void Application_Start(object sender, EventArgs e)
{
HttpContext incoming = HttpContext.Current;
string oldpath = incoming.Request.Path.ToLower();
incoming.Response.Filter = new System.IO.Compression.GZipStream(incoming.Response.Filter, System.IO.Compression.CompressionMode.Compress);
HttpContext.Current.Response.AppendHeader("Content-encoding", "gzip");
HttpContext.Current.Response.Cache.VaryByHeaders["Accept-encoding"] = true;
}
it donot gives error in visual studio. but when i put this code on IIS, it gives error/exception
Exception Details: System.Web.HttpException: Request is not available in this context
can anyone suggest or explain what should i do.
Application_Start is executed when your web application starts and this start is not associated with any page request, as page request is not happened yet at this point. Request is not available on Application_Start.
You can use Server.MapPath() instead.
The issue here is an elusive one, the built in Visual Studio cassini web server will be running requests using the older pattern of firing up the application upon the first request, which is the same as Managed Pipeline mode = classic in IIS. This means that there is a request object for you to access straight away, as the request is what triggered the app_start.
However, when you put this onto an IIS 7 box with a Managed pipeline mode = Integrated, it will fail. This is because an integrated pipe means that the site is started as soon as the app pool fires up, meaning there is no request object for it to hook into.
To solve this problem I'd recommend letting IIS compress the content rather than doing it by hand, this link has the details to get you started and here is a good outline about the difference it can make.
If you're really determined to do the compression within the application, I'd suggest implementing it as a HttpModule, similar to this example.
EDIT: Another implementation of a gzipping HttpModule here.
I recently found out that there are other domain names pointing to my website (that don't belong to me) and I was wondering how people can stop/prevent this from happening. I'm hosting this on peer1 using IIS and I'm using ASP.NET C#.
Can I use an HttpModule or some other code to reject domain names that aren't mine?
Is there a better way?
You should activate name-based virtual hosting and only show your real website for the desired domain names. For all other names, you can display a suitable error message.
Details: Your webserver is contacted by its IP address. There is nothing you can do to stop that. Anyone can say, "connect to that IP address". For instance, anyone can register new domain names to point to your server's IP address. However, inside the request, there is a field Host with a name like www.example.com.
Upon receiving the request, your server may choose to inspect the Host field and deliver different content depending on that value. In the simplest case, the server ignores the field entirely and always prints out the same content. But in a more sophisticated set-up, so called "name-based (virtual) hosting", the server chooses the content depending on the hostname.
This is how shared webhosts work: There's a single server, but depending on the requested hostname it spits out a different website for each name.
Therefore, if you want to tie your server content to your hostname, you have to tell your server to produce your website only for your desired name, and to produce a different (error) website for all other cases.
In Apache this is trivial to configure, just check their documentation; for IIS I wouldn't know but I imagine it's equally simple.
If your hosting environment is IIS and you have admin access to it. Set your default website to show an error page and then create a new site with the host header matching your domain to point to your website.
This is my solution. It really works fast and solved my problem.
Insert this code in your .htacces
RewriteCond %{HTTP_HOST} !^www.higueyrd.com$
RewriteRule ^/?(.*) http://www.higueyrd.com/$1 [QSA,R=301,L]
Just put your domain.
In IIS there is a setting called bindings that allows you to select which hostnames your website will respond to. This feature allows an instance of IIS to host mulitple websites on a single IP address.
If you want your site to only work for http://example.com/ and http://www.example.com/, you should set the bindings to only work for "example.com" and "www.example.com".
The exception here is if you are using SSL. If you are, IIS cannot determine the hostname and you will most likely have to use a dedicated IP address for your site. In that scenario, user608576's solution will work. Although, I would put that code in your Global.asax file:
<%# Application Language="C#" %>
<script runat="server">
void Application_BeginRequest(Object sender, EventArgs args)
{
HttpRequest request = HttpContext.Current.Request;
HttpResponse response = HttpContext.Current.Response;
if( (request.Url.Host != "example.com") && (request.Url.Host != "www.example.com") )
{
response.Clear();
response.Write("Unauthorized domain name: " + request.Url.Host);
response.End();
}
}
</script>
As a temporary fix you can do this . May be on home page load or BeginRequest .
if(!Request.Url.Host.ToLower().contains("mysite.com")){
Response.Redirect("error.html");
}
If i remember right when i last check my sites cpanel i saw a feature that stopped redirections to my domain if checked. I´m using Hostso as my host so check their test cpanel for it.
Hope it helps alittle atleast :)
Fredrik wirth
if you want to handle in code then do it in Global.asax in BeginRequest as below
void Application_BeginRequest(object sender, EventArgs e)
{
if (!context.Request.Url.Host.ToLower().Equals("www.mydomain.com"))
{
context.Rewritepath("/invalidpage.aspx");
}
}
The other simple way is to specify a host headers in IIS for your website.
http://technet.microsoft.com/en-us/library/cc753195(v=ws.10).aspx
Note: I am writing through my mobile so consider spelling mistakes
I have a custom site I'm building with automatic url rewriting using a custom engine. The rewriting works fine as long as the page url doesn't end in somehting like .htm or .html. For these pages it goes directly to the iis 404 page instead of hitting my rewriting engine first.
I have the * wildcard handler in the "Home Directory" section of the IIS6 configuration of that website but these urls seem to be ignored by it (altho things like css, jpg, js, etc to get sent to the url handler in my web project). How do i set up IIS6 to force these urls to get sent to the handler, while still serving the page if it exists normally?
The handler basically does this
if (!File.Exists(Request.Path))
{
doMyRewriting();
}
I have to assume that using a block like this (just and example, the real one does some other stuff to format the Request.Path to be proper with everything) should run the "doMyRewriting()" if the requested file does not exist otherwise it will serve the page normally. Am I mistaken?
If I tell IIS specifically to send .htm and .html pages thru to the .NET handler the rewriting works but if the page is actually there it will not serve it.
Any help would be greatly appreciated.
Thanks in advance!
Don't know if you can or would want to do this, but there is the Ionics Isapi url rewriter you can use.
http://www.codeplex.com/IIRF
Basically install that then set a rule to remove the .html that way it hits your rewrite engine. I use it on IIS 6 with several of my blogs.
I think if you are having IIS send all requests to .NET and your handler, then your handler will need to detect if the page exists and serve it instead of rewriting.
UrlRewriting.NET has an option to do this - you might want to look at their code to see how they're handling this case.
In my opinion, rewriting URLs with IIS 6 is best handled with an ISAPI filter written as unmanaged native code. Otherwise, you run into the issues you've mentioned - having to map all extensions to ASP.Net and losing the ability for simple file handling. With an ISAPI filter, you can choose to not rewrite some URLs and let IIS handle them as normal.
To get started, I suggest reading the ISAPI Filter Overview on MSDN.
If your filter absolutely needs the .Net framework runtime, it is possible to write a small ISAPI filter shell that hosts the CLR and forwards the requests to some managed code. The Filter.Net Framework takes this approach and may be suitable for your needs. There is the small drawback to this approach in that you will have to use the same .Net version as any ASP.Net applications that are run in the main IIS process.