Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading AjaxControlToolkit Scripts from Microsoft's CDN using ScriptManager/ToolkitManager

I know there is another question asking the same thing, but it hasn't gotten any attention for months now: https://stackoverflow.com/questions/3786088/how-to-force-ajax-control-toolkit-scripts-loading-from-cdn

I've upgraded my website to .NET4, and I'm now using the scriptManager's EnableCDN="true" tag. My Ajax scripts are being referenced from the Microsoft CDN just how I expected, but I can't seem to get my AjaxControlToolkit scripts to load from the CDN. Instead they all load locally through ScriptResource.axd.

I know where the CDN files are located, and I know that I could reference those files every time I use a control, but I've got a lot of legacy code that loads on its own from the scriptmanager, just pulling ScriptResource.axd files.

Any suggestions how to get the control toolkit scripts to load from the CDN? I already have the standard WebForms.js, etc.

Let me know if there is anything I can clear up, here is the script manager that I'm using: (I have tried the toolkitscriptmanager)

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" 
EnableCdn="true" EnableScriptLocalization="false" 
LoadScriptsBeforeUI="false" EnableViewState="false" />
like image 567
Brett Avatar asked Dec 09 '22 10:12

Brett


2 Answers

If an assembly does not define a CDN path in its WebResourceAttributes, EnableCDN won't know where to go. So there's that.

However, you can define your own CDN path if you know of a CDN that has them. I honestly don't know whether the latest ACT is on the CDN or not. If the version you are using is 40412, then yes, it is: http://www.asp.net/ajaxlibrary/CDNACT40412.ashx

The way you define your own path is described in my blog entry (also mentioned in another answer):
http://weblogs.asp.net/infinitiesloop/archive/2009/11/23/asp-net-4-0-scriptmanager-improvements.aspx

Here's an example, explained below:

void Application_Start(object sender, EventArgs e) {
    ScriptManager.ScriptResourceMapping.AddDefinition("Foo.js", typeof(FooControl).Assembly, new ScriptResourceDefinition {
        ResourceName = "Foo.js",
        ResourceAssembly = typeof(FooControl).Assembly,
        CdnPath = "http://yadda-yadda/foo.js",
        CdnDebugPath = "http://yadda-yadda/foo.debug.js",
    });
}

Here is what is happening. First of all, there exists an assembly, lets call it Foo.dll. It contains a script as an embedded resource named "Foo.js" (the .js extension is optional). So, asp:ScriptReferences (on the page or coming from a server control) to that resource will go to ScriptResource.axd. The assembly's WebResourceAttribute for the script does not define a CDN path, so it still goes there if you turn on CdnMode.

You want to fix that, so what you do is target that script. To do so, you have to refer to its assembly and name. Together, they identify the script (it's "primary key", if you will). The 1st two parameters to AddDefinition accomplish that.

Now that you targetted it, you get to completely redefine what it means to be that script. You put it in another assembly, another path, define the cdn path.. anything.

The ResourceName and ResourceAssembly properties of the definition establish that the script really still belongs to the same assembly it came from. That might seem unnecessary if you are going to load from CDN anyway. But it does two things: (1) It will still work if you turn off CDN, and (2) it will still be able to figure out if a debug version of the resource (foo.debug.js) or any LOCALIZED versions (e.g. foo.fr-FR.js) exist in the assembly, and thereby know whether the CDN will contain that version (since it can't very well check that from the server). So it's a good idea to give the definition an owner (note that it will NOT default to the original owner if you don't specify them).

The CdnPath and CdnDebugPath properties are obvious, and your ultimate goal here. EnableCdn should now work!

I encourage you to use ScriptMapping when possible to define the details about a script and then refer to them by name instead of path. Keeps the markup simple and it's more DRY since you get to define all the details for the script in one place.

NOW... How do you do that with the AjaxControlToolkit?

You're going to have to write one of these mappings for each and every script in the toolkit. That is not going to be fun, there are dozens and dozens of them. Don't make the mistake of writing separate ones for each release and debug script though. The two are a pair and are defined with 1 mapping. There are also localized versions of some of them, but I wouldn't bother with that -- the messages are error messages meant for developers, which really don't need to be localized anyway, because that feature is meant to localize content for end users.

One thing -- try it out on 1 or 2 of the ACT scripts to make sure you got it right. Also, there's one more complexity here I didn't mention.

Remember when I said the Name and Assembly come together to identify a script (its "primary key")? That's true, but there's one special rule for scripts that come from System.Web.Extensions OR the so-called "AjaxFrameworkAssembly". In that case, specifying a null assembly, or using the overload where that parameter is missing altogether, is equivalent. What is a "AjaxFrameworkAssembly"? Well, System.Web.Extensions contains a little-known feature that allows an assembly to assert itself as the "AjaxFrameworkAssembly" via an assembly level attribute. Doing that means "I am the assembly that contains ms ajax scripts, you should come to me instead of System.Web.Extensions". The ACT uses this in order to 'upgrade' the scripts that are embedded in System.Web.Extensions. It has its own copies of those scripts that have a later version number. All you have to do is reference the assembly, and presto, the scripts are automatically redirected to come from there instead. The point here being, you may be able to get away with not specifying the assembly in all of those mappings. But, try it out, don't take my word for it.

This has been an InfinitiesLoop answer. I now return you to your regularly scheduled programming :)

like image 67
InfinitiesLoop Avatar answered May 09 '23 09:05

InfinitiesLoop


I too struggled with this issue and eventually found out that the ACT-version of the scripts are also placed on the CDN however, no documentation states that they are.

The root path is: http://ajax.aspnetcdn.com/ajax/act/40412/

For example the path to the ACT-version of the MicrosoftAjaxCore.js file would be: http://ajax.aspnetcdn.com/ajax/act/40412/MicrosoftAjaxCore.js

To make sure that this path is always used - is performed once when the application loads and is then used on every page that uses the ToolkitScriptManager - use the following lines of code in Global.asax. Note that you need to a code block/script file.

ScriptManager.ScriptResourceMapping.AddDefinition("MicrosoftAjaxCore.js", new ScriptResourceDefinition
    {
        ResourceName = "MicrosoftAjaxCore.js",
        CdnPath = "http://ajax.aspnetcdn.com/ajax/act/40412/MicrosoftAjaxCore.js",
        CdnDebugPath = "http://ajax.aspnetcdn.com/ajax/act/40412/MicrosoftAjaxCore.debug.js"
    });

I also configured my ScriptManager with the attribute FrameworkMode="Disabled" and the specified which files I needed, linking them towards the ACT-version, otherwise the version from System.Web.Extensions is used. Make sure that you point out the correct version of ACT.

<asp:ScriptReference Name="MicrosoftAjaxCore.js" Assembly="AjaxControlToolkit, Version=4.1.51116.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
<asp:ScriptReference Name="MicrosoftAjaxComponentModel.js" Assembly="AjaxControlToolkit, Version=4.1.51116.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e"  />
<asp:ScriptReference Name="MicrosoftAjaxSerialization.js" Assembly="AjaxControlToolkit, Version=4.1.51116.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e"  />
<asp:ScriptReference Name="MicrosoftAjaxNetwork.js" Assembly="AjaxControlToolkit, Version=4.1.51116.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e"  />
<asp:ScriptReference Name="MicrosoftAjaxWebServices.js" Assembly="AjaxControlToolkit, Version=4.1.51116.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e"  />
<asp:ScriptReference Name="MicrosoftAjaxWebForms.js" Assembly="AjaxControlToolkit, Version=4.1.51116.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
like image 27
Marcus Lundberg Avatar answered May 09 '23 09:05

Marcus Lundberg