声明:本文是ASP.NET 白皮书ASP.NET 4 Breaking Changes的阅读摘要,只是本人的学习记录,并非完整翻译,仅供参考,由于水平有限,有些翻译未必准确。点击下载PDF文档。
--------------------------------------------
这些重大的变化可能会影响使用之前版本开发的应用程序。
为了使控件的呈现更符合Web标准,ASP.NET 修改了服务器控件。其中一些控件在旧版本中会生成无法禁止的标记,现在在 ASP.NET 4 中默认已经不再生成了。
为了防止这种变化对旧应用程序通过改变 IIS 应用程序池升级到 .NET Framework 4 带来的影响,可以在 Web.config 文件的 pages 节添加这样的属性:
<pages controlRenderingCompatibilityVersion="3.5" />
这样项目中的控件还会保留之前的呈现方式。主要的呈现变化如下:
ASP.NET 4 中的 ClientIDMode 设置可指定如何为 HTML 元素生成 id 属性。旧版本中默认行为等价于把 ClientIDMode 设置为 AutoID,而现在默认是 Predictable。
如果想让旧项目保持原先的行为,可以在 Web.config 中这样设置:
<pages ClientIDMode="AutoID" />
在 ASP.NET 4 中,HttuUtility 和 HttpServerUtility 类的 HtmlEncode 和 UrlEncode 方法已经更新到可以对单引号(')进行编码
ASP.NET 4 中对页面(.aspx)和用户控件(.ascx)的解析器更加严格,它会拒绝更多非法标记的实例。
下面的例子在早期版本可以成功解析但在 ASP.NET 4 中会引发解析错误:
<asp:HiddenField runat="Server" ID="SomeControl" Value="sampleValue" ; />
<asp:LinkButton runat="server" ID="SomeControl" onclick="someControlClicked"
style="display:inline; CssClass="searchLink" />
浏览器定义文件已经更新为包含了有关新的和更新的浏览器和设备的信息。旧版本的浏览器如 Netscape Navigator 已经被移除,而更新的浏览器和设备如 Google Chrome 和 Apple iPhone 已经添加。
如果你的应用程序中包含了依赖被移除的浏览器定义的自定义浏览器定义文件,为了防止出错,可以把 .NET 2.0 中的浏览器定义文件拷贝到 ASP.NET 4 中对应的文件夹下,然后运行 aspnet_regbrowsers.exe 命令行工具。
在旧版本的 ASP.NET 中,根 Web.config 的 assembly 节包含对 System.Web.Mobile.dll 程序集的引用。为了提高性能,这个引用目前已经被移除。
System.Web.Mobile.dll 程序集包含在 ASP.NET 4 中,但已经废弃。
ASP.NET 中的请求验证特性提供了防止跨站点脚本(XSS)攻击的保护机制。在旧版本中,请求验证默认是启用的,但它仅当页面运行时才应用到页面上(.aspx 文件及其类文件)。
在ASP.NET 4中,请求验证默认是对所有请求都启用的,因为它是在 HTTP 请求的 BeginRequest 阶段启用的。这样请求验证就应用到了对所有 ASP.NET 资源的请求上,而不仅仅是 .aspx 页面请求上。这包括了 Web 服务调用和自定义 HTTP 处理程序。当读取 HTTP 请求内容的自定义 HTTP 模块时请求验证同样会激活。
如果希望继续使用旧版本的请求验证模式,需要在 Web.config 文件中配置:
<httpRuntime requestValidationMode="2.0" />
ASP.NET 同时使用加密和哈希算法来辅助保护数据,例如表单验证cookie和视图状态。旧版本的ASP.NET 使用老的 HMACSHA1 算法,而 ASP.NET 4 现在默认使用 HMACSHA256 算法对 cookie 和视图状态进行哈希操作。
如果不希望影响运行在混合的 ASP.NET 2.0/ASP.NET 4 环境而数据必须跨.NET Framework版本工作的应用程序,可以配置ASP.NET 4 Web 应用程序使用老的 HMACSHA1 算法:
<machineKey validation="SHA1" />
过去在 ASP.NET 3.5 应用程序的 Web.config 中常用的配置信息在 .NET Framework 4 中已经更新到了machine.config 文件和根 Web.config 文件中。由于托管的 IIS 7 和 IIS 7.5 配置系统的复杂性,在 ASP.NET 4 下和在 IIS7 和 IIS7.5 下运行 ASP.NET 3.5 应用程序会导致 ASP.NET 或 IIS 配置错误。
推荐使用 VS2010 中的项目升级工具把 ASP.NET 3.5 应用程序升级到 ASP.NET 4。VS2010 会自动地修改 ASP.NET 应用程序中的 Web.config 文件来包含针对 ASP.NET 4 的合适的设置。
否则就要手工修改应用程序的 Web.config 文件。
(评:看到这里,我发现手工解决非常麻烦,绝对是一种折磨,详见http://www.asp.net/whitepapers/aspnet4/breaking-changes#0.1__Toc256770149)
即父应用程序运行在 ASP.NET 2.0/3.5 下,而它的子应用程序(虚拟路径)运行在ASP.NET 4下,这会导致子启动时报错。
(评:解决方法同样非常麻烦,详见http://www.asp.net/whitepapers/aspnet4/breaking-changes#0.1__Toc256770149)
运行SharePoint的Web服务器会把 web.config 部署在SharePoint网站根目录下(如C:\inetpub\wwwroot\web.config)。在这个配置文件中,SharePoint设置了一个自定义的部分信任级别,叫做 WSS_Minimal。
而 ASP.NET 4 的代码访问安全(CAS)基础设施需要寻找的是名为 ASP.NET 的许可设置。这样就冲突了。
当前没有一个版本的 SharePoint 和 ASP.NET 兼容,所以要避免 ASP.NET 4 网站作为 SharePoint 网站的子站点运行。
旧版本的ASP.NET包含一个 PathInfo 值,返回不同的与文件路径相关的属性,包括 HttpRequest.FilePath, HttpRequest.AppRelativeCurrentExecutionFilePath 和 HttpRequest.CurrentExecutionFilePath。ASP.NET 不再从这些属性的返回值中包含 PathInfo 值。代替地,PathInfo 信息在 HttpRequest.PathInfo 中可用。例如,对于下面的 URL 片断:
/testapp/Action.mvc/SomeAction
在早版本的ASP.NET中,HttpRequest 属性拥有下列值:
HttpRequest.FilePath: /testapp/Action.mvc/SomeAction
HttpRequest.PathInfo: (空)
在 ASP.NET 4 中,HttpRequest 属性的代替值:
HttpRequest.FilePath: /testapp/Action.mvc
HttpRequest.PathInfo: SomeAction
在 ASP.NET 4 在 IIS6 上启用之后,运行其中(在 Windows Server 2003 或 Windows Server 2003 R2 中)的 ASP.NET 2.0 应用程序可能会产生像下面这样的错误:
System.Web.HttpException: Path '/[yourApplicationRoot]/eurl.axd/[Value]' was not found.
这个错误之所以会发生,是因为当 ASP.NET 检测到一个网站被配置为使用 ASP.NET 4 的时候,ASP.NET 4 的原生组件会把无扩展名的 URL 传递给 ASP.NET 的托管部分作进一步处理。然而,如果如果在 ASP.NET 4 网站下面的虚拟目录是配置为使用 ASP.NET 2.0,处理无扩展名 URL 会带来一个包含字符串 "eurl.axd" 的被修改的 URL。这个修改的URL随后发送给ASP.NET 2.0 应用程序。ASP.NET 2.0 不能识别 "eurl.axd" 格式。因此,ASP.NET 2.0 会试图查找一个名为 eurl.axd 的文件并执行它。因为没有这样的文件存在,所以请求会失败并引发 HttpException 异常。
解决方法1:如果ASP.NET 4 不是必须用来运行网站,就将网站重新映射到 ASP.NET 2.0。
解决方法2:如果 ASP.NET 4 是必须用来运行网站,那么就将任意 ASP.NET 2.0 虚拟目录移到另一个映射到 ASP.NET 2.0 的网站下。
解决方法3:如果上述两者的做法太不实际,那么就显式地禁止 ASP.NET 4 中的无扩展名URL处理。使用下述步骤:
1) 在注册表中,打开这个节点:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0
(1) 创建一个 DWORD 值,名为 EnableExtensionlessUrls
(2) 设置 EnableExtensionlessUrls 为 0。这会禁用无扩展名URL行为
(3) 保存注册表值关闭注册表编辑器
(4) 运行 iisreset 命令行工具,这会让 IIS 读取注册表的新值
注意:设置 EnableExtensionlessUrls 为 1 会启用无扩展名URL行为。这个值如果未指定是默认值。
ASP.NET 4 包含了一些修改,改变了当无扩展名 URL 解析到默认文档时 HTML form 元素的 action 属性是如何呈现的。一个解析到默认文档的无扩展名 URL 的示例是 http://contoso.com/,会导致请求到 http://contoso.com/Default.aspx。
当请求是对映射到默认文档的无扩展名 URL 的时候,ASP.NET 4 现在会把 HTML form 元素的 action 属性值呈现为一个空字符串。例如,在旧版本的 ASP.NET 中,对 http://contoso.com 的请求会导致对 Default.aspx 的请求。在这个文档中,form 开标签会被呈现为这样:
<form action="Default.aspx" />
在 ASP.NET 4 中,对 http://contoso.com 的请求同样会导致对 Default.aspx 的请求,然而 ASP.NET 现在会把 form 开标签呈现为这样:
<form action="" />
这个区别会在表单POST如何被IIS和ASP.NET处理方面产生微妙的变化。当 action 属性是一个空字符串时,IIS 的 DefaultDocumentModule 对象会创建一个对 Default.aspx 的子请求。在大多数情况下,这个子请求对应用程序代码来说是透明的,Default.aspx 页面运行正常。
然而,当子请求过程中,在托管代码和 IIS7或IIS7.5集成模式之间的一个潜在交互会导致托管的.aspx页面停止工作。如果下列条件发生,对 Default.aspx 文档的子请求会导致错误或未预料的行为:
1) form 元素的 action 属性设置为"" 的 .aspx 页面发送到浏览时
2) 表单回送(post back)到 ASP.NET
3) 托管的 HTTP 模块读取实体主体的一部分时。例如,一个模块读取 Request.Forms 或 Request.Params。这导致 POST 请求的实体主体被读取到托管内存中。结果实体主体对任何运行在IIS7或IIS7.5集成模式中运行的本机代码模块都不再可用了。
4) IIS 的 DefaultDocumentModule 对象最终会运行并创建一个对 Default.aspx 文档的子请求。然而,因为实体主体已经被一部分托管代码读取,没有可用的实体再发送到子请求。
5) 当 HTTP 管道对子请求运行时,对 .aspx 文件的处理程序会在处理程序阶段运行
6) 因为没有实体主体,没有表单变量也没有视图状态,因此没有可用信息给 .aspx 文件处理程序来确定什么事件要被触发。这样就没有会影响 .aspx 文件的回送事件处理程序运行。
用下列方法解决这个行为:
ASP.NET 2.0 和添加了扩展功能的 ASP.NET 3.5,使用 .NET Framework 1.1 和 2.0 的代码访问安全(CAS)。然而,在 ASP.NET 4 中 CAS 的实现发生了本质的改变。这样,依赖运行在全局程序集缓存(GAC)信任代码的部分信任的 ASP.NET 应用程序可能会因各种安全异常而失败。对机器 CAS 策略做出扩展修改依赖的部分信任的应用程序也可能会因安全异常而失败。
可以把部分信任的 ASP.NET 4 应用程序恢复到 ASP.NET 1.1 和 2.0 的行为,使用 trust 配置元素的新的 legacyCasModel 属性:
<trust level= "Medium" legacyCasModel="true" />后面的细节看原文吧: http://www.asp.net/whitepapers/aspnet4/breaking-changes#0.1__Toc256770155。
一些用于 ASP.NET 成员的类型已经从 System.Web.dll 移到新的 System.Web.ApplicationServices.dll 程序集。这是为了解决架构分层时在客户端中的类型和扩展的 .NET Framework SKU 中类型之间的依赖。
网站项目不会因移动类型而带来问题,因为 System.Web.ApplicationServices.dll 被添加到了引用程序集列表中,会被 ASP.NET 编译系统默认使用。如果通过在VS2010中打开的方式把旧版本ASP.NET创建的网站项目升级到 ASP.NET 4,项目不会出错而编译会成功。
类似地,如果以同样的方式把旧版本的ASP.NET Web 应用程序升级到 ASP.NET 4,升级过程会把对 System.Web.ApplicationServices.dll 的引用添加到项目中,这样升级后的 Web 应用程序同样不会报而编译会成功。
使用旧版本ASP.NET创建的编译后的(二进制)文件在ASP.NET 4中运行也不会报错,尽管会员类型移到了不同的程序集。类型转向信息已经添加到了 ASP.NET 4 版本的 System.Web.dll 上,这会自动把运行时引用路由到新类型的位置。
然而,使用特定会员类型的类库从旧版本的 ASP.NET 升级后用在 ASP.NET 4 项目中会编译失败。例如,一个类库项目可能会编译失败并报告类似下面的错误:
The type 'System.Web.Security.MembershipUser' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
The type name 'MembershipUser' could not be found. This type has been forwarded to assembly 'System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. Consider adding a reference to that assembly.
可以通过在类库项目中添加对 System.Web.ApplicationServices.dll 的引用来解决这个问题。
下面的列表展示了从 System.Web.dll 移到 System.Web.ApplicationServices.dll 的 System.Web.Security 类型:
在 ASP.NET 1.0 中,一个 bug 导致指定了 Location="ServerAndClient" 的缓存页面成为了一个设置了在响应中发出 Vary:* HTTP 头的输出缓存。这会带来告诉客户浏览器永不在本地缓存页面的影响。
在 ASP.NET 1.1 中,System.Web.HttpCachePolicy.SetOmitVaryStar 方法被添加,它可以被调用来阻止 Vary:* 头。选择这个方法是因为改变生成的 HTTP 头会被认为是一个潜在的重大变化。然而,开发人员会被ASP.NET 中的行为困扰, bug 报告暗示开发人员对存在的 SetOmitVaryStar 行为并不了解。
在 ASP.NET 4 中,决定修复这个根问题。Vary:* HTTP头不再从指定下列指定的响应发出:
<%@ OutputCache Location="ServerAndClient" %>
这样,SetOmitVaryStar 就不再需要用来阻止 Vary:* 头了。
在页面的 @OutputCache 指令中指定了 Location="ServerAndClient" 的应用程序中,可以看到这个行为隐含了 Location 属性值的名字——即,页面将不需要调用 SetOmitVarStar 方法就会缓存在浏览器中。
如果页面必须发出 Vary:*,调用 AppendHeader 方法,例如:
HttpResponse.AppendHeader("Vary", "*");
也可以把输出缓存的 Location 属性改为 "Server"。
集成到 ASP.NET 2.0 的 Passport 支持已经被废弃和不被支持有几年了,因为 Passport(现在叫 LiveID)发生了变化。所以,在 System.Web.Security 中的和 Passport 相关的 5 个类型现在已经用 ObsoleteAttribute 特性标记了。
在 ASP.NET 3.5 中,MenuItem.PopoutImageUrl 属性让你能够为图像指定 URL,显示在菜单项中表示菜单项有一个动态的子菜单。例如:
<asp:menu id="NavigationMenu"
staticdisplaylevels="1"
staticsubmenuindent="10"
orientation="Vertical"
target="_blank"
runat="server">
<items>
<asp:menuitem navigateurl="default2.aspx"
text="Home"
PopOutImageUrl="~/Images/Popout.gif"
tooltip="Home">
<asp:menuitem navigateurl="default3.aspx"
text="Movies"
PopOutImageUrl="~/Images/Popout.gif"
tooltip="Movies">
</asp:menuitem>
</asp:menuitem>
</items>
</asp:menu>
在 ASP.NET 4 中设计发生了变化,没有输出会为 PopOutImageUrl 呈现。相反,你必须直接在 Menu 控件中使用 StaticPopOutImageUrl 属性或 DynamicPopOutImageUrl 属性指定图像URL。当你使用静态菜单时, Menu.StaticPopOutImageUrl 属性指定了图像的 URL,它会显示出来表示静态菜单项有一个子菜单,例如:
<asp:menu id="NavigationMenu"
staticdisplaylevels="1"
staticsubmenuindent="10"
orientation="Vertical"
target="_blank"
StaticPopOutImageTextFormatString="More..."
StaticPopOutImageUrl="Images/Popout.gif"
runat="server">
<items>
<asp:menuitem navigateurl="default2.aspx"
text="Home"
tooltip="Home">
<asp:menuitem navigateurl="default3.aspx"
text="Movies"
tooltip="Movies">
</asp:menuitem>
</asp:menuitem>
</items>
</asp:menu>
如果使用动态菜单,使用 Menu.DynamicPopOutImageUrl 属性为图像指定URL,表示动态菜单项有一个子菜单。例如:
<asp:menu id="NavigationMenu"
staticdisplaylevels="1"
staticsubmenuindent="10"
orientation="Vertical"
target="_blank"
DynamicPopOutImageTextFormatString="More..."
DynamicPopOutImageUrl="Images/Popout.gif"
runat="server">
<items>
<asp:menuitem navigateurl="default2.aspx"
text="Home"
tooltip="Home">
<asp:menuitem navigateurl="default3.aspx"
text="Movies"
tooltip="Movies">
</asp:menuitem>
</asp:menuitem>
</items>
</asp:menu>
如果 Menu.DynamicPopOutImageUrl 属性没有设置而 Menu.DynamicEnableDefaultPopOutImage 属性设置为 false,不会有图像显示。类似地,如果 StaticPopOutImageUrl 属性没有设置而 StaticEnableDefaultPopOutImage 属性设置为 false,不会有图像显示。
当设置这些属性的路径时,要使用正斜杠(/)而不是反斜杠(\)。
在 ASP.NET 4 中,使用 Menu.StaticPopOutImageUrl 和 Menu.DynamicPopOutImageUrl 属性指定的图像,如果路径中包含反斜杠(\),将不会显示。这是对旧版本 ASP.NET 的改变。
注意从旧版本ASP.NET移植到ASP.NET 4的应用程序也会受到影响,因为 MenuItem.PopOutImageUrl 已经改变了。