65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.20/5 (8投票s)

2007年7月20日

CPOL

3分钟阅读

viewsIcon

60366

downloadIcon

476

一个智能表单控件,修复了 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 属性是如何写入的,我们可以看到由于 GetActionAttributeprivate 的,因此无法在它到达 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 设计器。 我尚未成功为此创建一个自定义设计器。 如果有人为此设计器,请给我发电子邮件,我会将其添加到文章中并提供适当的署名。

© . All rights reserved.