ASP.NET WebForms的一个主要抱怨是框架从开发人员手中夺走了呈现文档的控制权——无数组件(既包括内置于ASP.NET中的,也包括第三方的)能够将它们自己的
这是我目前使用反射的解决方案 - 它不太美观,但现在它能工作:
public static class WebResourceOverrides
{
private const BindingFlags PrivateThis = BindingFlags.NonPublic | BindingFlags.Instance;
public static void OverrideClientScript(Page page, String key, String newContent)
{
if( page == null ) throw new ArgumentNullException(nameof(page));
if( key == null ) throw new ArgumentNullException(nameof(key));
if( newContent == null ) throw new ArgumentNullException(nameof(newContent));
Type clientScriptType = page.ClientScript.GetType();
FieldInfo clientScriptBlocksField = clientScriptType.GetField( "_clientScriptBlocks", PrivateThis);
FieldInfo registeredClientScriptBlocksField = clientScriptType.GetField( "_registeredClientScriptBlocks", PrivateThis );
ArrayList clientScriptBlocks = (ArrayList)clientScriptBlocksField.GetValue( page.ClientScript );
ListDictionary registeredClientScriptBlocks = (ListDictionary)registeredClientScriptBlocksField.GetValue( page.ClientScript );
// clientScriptBlocks contains `System.Tuple<ScriptKey,String,Boolean>`, unfortunately `ScriptKey` is internal, so we can't play with the `Tuple` value directly.
// ...but we can still access the fields:
foreach(Object tuple in clientScriptBlocks)
{
Boolean found = ProcessScriptRegistration( tuple, key, newContent );
if( found ) break;
}
}
private static Boolean ProcessScriptRegistration(Object tuple, String key, String newContent)
{
Type tupleType = tuple.GetType();
FieldInfo scriptKeyField = tupleType.GetField("m_Item1", PrivateThis );
FieldInfo htmlField = tupleType.GetField("m_Item2", PrivateThis); // Thankfully you can overwrite a `readonly` field using Reflection
Object scriptKeyValue = scriptKeyField.GetValue( tuple );
String html = (String)htmlField.GetValue( tuple );
Type scriptKeyType = scriptKeyValue.GetType();
FieldInfo scriptKeyKeyField = scriptKeyType.GetField("_key", PrivateThis );
String scriptKeyKey = (String)scriptKeyKeyField.GetValue( scriptKeyValue );
if( scriptKeyKey == key )
{
htmlField.SetValue( tuple, newContent );
return true;
}
return false;
}
}
用法:
我从.master
页面的OnPreRender
方法中调用它,以确保如果页面使用了错误的jQuery脚本,则此时它将被添加到内部注册列表中(因为它通常是由库的控件的Init
或Load
事件处理程序添加的):
public class MyMasterPage : MasterPage {
protected override void OnPreRender(EventArgs e) {
base.OnPreRender( e );
WebResourceOverrides.OverrideClientScript( this.Page, "jquery", "<!-- jQuery 1.4.1 disabled. -->" );
}
}
<form>
渲染如下。原来通过 WebResource.axd
引用 jQuery1.4.1.js
文件的 <script>
块将被渲染,现在被替换为注释。<form ...>
<script src="/WebResource.axd?d=1234..."></script>
<script src="/WebResource.axd?d=5678..."></script>
<!-- jQuery 1.4.1 disabled. -->
<script src="/WebResource.axd?d=90AB..."></script>
我的代码没有删除注册信息,因为客户端脚本注册的内部状态使用了一个整数索引的ArrayList
,所以删除一个条目可能会破坏任何存储的索引 - 这意味着糟糕的库控件仍然认为它们的jQuery版本被引用了(因为它们中的一些调用了this.Page.ClientScript.IsClientScriptIncludeRegistered
)。
MyJQuery.1.4.1.js
的资源替换为 JQuery 1.6.2?保留名称,更新代码。 - John WuRegisterClientScript
,则会被忽略,无论脚本正文是否不同。 - Dai