ASP.NET URL 重写的智能表单控件






4.20/5 (8投票s)
一个智能表单控件,修复了 ASP.NET 中无扩展名 URL 重写引起的许多问题。
引言
我工作的一个网站使用了 URL 重写,例如,当您输入一个地址 www.somesite.com/Products/WidgetA/ 时,您实际上是在浏览网站中的另一个 ASP.NET 页面,例如: www.somesite.com/ProductDisplay.aspx?page=Products/WidgetA/。
有关 URL 重写的完整解释以及一个非常有用的重写引擎,请访问 15seconds.com 并阅读 Robert Chartier 撰写的 Rewrite.NET -- A URL Rewriting Engine for .NET。
背景
这样做的一个问题,相信我,URL 重写有很多问题,就是它会破坏 form
标签。 form
标签的 action
将是 .aspx 页面的真实地址,而不是您想要的友好的地址。 为了解决这个问题,MSDN 建议您创建一个“无 Action 的表单”,也就是说,您创建一个自定义的 form
标签,该标签根本不写 action
属性在 form
标签上。
namespace ActionlessForm {
public class Form : System.Web.UI.HtmlControls.HtmlForm
{
protected override void RenderAttributes(HtmlTextWriter writer)
{
writer.WriteAttribute("name", this.Name);
base.Attributes.Remove("name");
writer.WriteAttribute("method", this.Method);
base.Attributes.Remove("method");
this.Attributes.Render(writer);
base.Attributes.Remove("action");
if (base.ID != null)
writer.WriteAttribute("id", base.ClientID);
}
}
}
//From URL Rewriting in ASP.NET by Scott Mitchell
上述代码的主要问题是它完全破坏了任何钩住回发周期的服务器控件(例如验证器)的 form
标签。 如果您使用优秀的 Lutz Reflector 检查 .NET Framework 的 HtmlForm
类的 RenderAttributes
方法的源代码,您将看到 .NET Framework 的 HtmlForm
类中发生了很多事情,而 Scott 的版本并没有复制。 除此之外,如果 HtmlForm
标签在未来版本的 .NET Framework 中发生了变化怎么办?
如果我们确切地检查 action
属性是如何写入的,我们可以看到由于 GetActionAttribute
是 private
的,因此无法在它到达 HtmlTextWriter
之前直接更改 action
属性值。
此外,使用派生类无法简单地复制 RenderAttributes
方法中的某些逻辑,因为许多必要的属性和方法是 private
的。
writer.WriteAttribute("action", this.GetActionAttribute(), true);
因此,我们需要采取不同的方法来控制 Form
标签 action
属性是否以及如何写入。
使用代码
我的方法是在将属性写入 HtmlTextWriter
之前拦截它。 为了做到这一点,我创建了一个名为 SelectiveHtmlTextWriter
的派生类并重写了 WriteAttribute
方法
public class SelectiveHtmlTextWriter : HtmlTextWriter
{
...
public override void WriteAttribute(string name, string value, bool fEncode)
{
base.WriteAttribute(name, "/newpagename/goes/here/", fEncode);
}
}
public class SmartForm : HtmlForm
{
...
protected override void RenderAttributes(HtmlTextWriter writer)
{
SelectiveHtmlTextWriter customWriter = new SelectiveHtmlTextWriter(writer);
base.RenderAttributes(customWriter);
}
}
创建一个新的 SelectiveHtmlTextWriter
对象并将其传递给 HtmlForm
类的 RenderAttribute
方法,以代替普通的 HtmlTextWriter
。
这之所以有效,是因为 多态性。 HtmlForm
类关心的只是它获得对 HtmlTextWriter
的引用。 但是,当它调用 RenderAttribute
时,我的重写会被调用。
这非常好用,但我从来不喜欢硬编码的值,因此为了使其更具扩展性,我在新的 SelectiveHtmlTextWriter
中添加了一个名为 WritingAttribute
的事件,SmartForm
类订阅该事件。 这允许在写入其值之前修改任何属性,特别是 action
。 在这种情况下,我正在替换 Request.RawUrl
值,该值保存了请求来自的实际 URL。
public class SmartForm : HtmlForm
{
public SmartForm()
: base()
{
}
protected override void RenderAttributes(HtmlTextWriter writer)
{
SelectiveHtmlTextWriter customWriter = new SelectiveHtmlTextWriter(writer);
customWriter.WritingAttribute +=
new SubstituteValueEventHandler(customWriter_WritingAttribute);
base.RenderAttributes(customWriter);
}
void customWriter_WritingAttribute(object sender, SubstituteValueEventArgs e)
{
if (e.Name == "action")
{
e.NewValue = Context.Request.RawUrl;
}
}
}
总之,如果我能选择,我根本不会使用 URL 重写。 我会找到其他方法让客户满意。 但是,我们在这里概述的 SmartForm
将大大减轻痛苦。 最后一个警告,使用 SmartForm
会破坏 Visual Studio 设计器。 我尚未成功为此创建一个自定义设计器。 如果有人为此设计器,请给我发电子邮件,我会将其添加到文章中并提供适当的署名。