2009-01-10 71 views
122

我试图构建一个非常非常简单的“微型web应用程序”,如果我完成了这个任务,我怀疑它会对几个Stack Overflow'rs感兴趣。我将它放在我的深度网站C#中,这是一个香草ASP.NET 3.5(即不是MVC)。如何在ASP.NET中进行更多控制?

流程很简单:

  • 如果用户输入一个URL的应用程序,它没有指定的所有参数(或其中的任何无效)我想只显示用户输入控制。 (只有两个。)
  • 如果用户输入与确实具有所有必需的参数,我想显示的结果输入控件(使他们能够改变参数)
  • 一个URL的应用程序

这里是我的自我强加的要求(设计和实施的混合物):

  • 我想提交使用GET而不是POST,主要是让用户可以轻松创建书签页面。
  • 不要希望网址最终在提交后看起来很愚蠢,无关的零碎和碎片。请只提供主要网址和真实参数。
  • 理想情况下,我想避免需要JavaScript。这个应用程序没有很好的理由。
  • 我希望能够在渲染时间和设置值等期间访问控件。特别是,我希望能够将控件的默认值设置为传入的参数值,如果ASP.NET不能自动为我执行此操作(在其他限制内)。
  • 我很高兴自己做所有的参数验证,并且我不需要太多的服务器端事件。这是非常简单的设置在页面加载的一切,而不是附加事件,按钮等的

这其中大部分是好的,但我还没有发现的完全删除视图状态和保持的其余任何方式有用的功能。使用this blog post的帖子,我设法避免为视图状态获取任何的值 - 但它仍然作为URL上的参数结束,这看起来非常难看。

如果我使它成为一个普通的HTML表单而不是ASP.NET表单(即取出runat="server"),那么我没有获得任何魔术视图状态 - 但是我无法以编程方式访问控件。

可能通过忽略大部分ASP.NET并使用LINQ to XML构建XML文档并执行IHttpHandler来完成所有这些工作。虽然这感觉有点低。

我意识到我的问题可以通过放宽我的约束(例如使用POST和不关心剩余参数)或使用ASP.NET MVC来解决,但是我的要求真的不合理吗?

也许ASP.NET只是不缩放下降到这种类型的应用程序?尽管有一个非常可能的选择:我只是很愚蠢,并且有一个非常简单的做法,我只是没有找到。

任何想法,任何人? (提示如何强大的堕落等等。这很好 - 我希望我从未声称自己是ASP.NET专家,因为事实恰恰相反......)

+15

“提示如何强大的堕落” - 我们都是无知的,只是不同的事情。我只是最近才开始参加这个活动,但我更钦佩这个问题。你显然还在思考和学习。荣誉给你。 – duffymo 2009-01-10 16:54:06

+15

我不认为我会注意那些放弃学习的人:) – 2009-01-10 17:05:16

+1

在一般情况下是正确的。计算机科学非常真实。 – 2009-01-10 17:14:17

回答

74

该解决方案将为您提供对控件的完整编程访问权限,包括控件的所有属性。此外,只有在文本框中的值将出现在在提交网址,以便您的GET请求的URL会在你更“有意义”的

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JonSkeetForm.aspx.cs" Inherits="JonSkeetForm" %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"> 
    <title>Jon Skeet's Form Page</title> 
</head> 
<body> 
    <form action="JonSkeetForm.aspx" method="get"> 
    <div> 
     <input type="text" ID="text1" runat="server" /> 
     <input type="text" ID="text2" runat="server" /> 
     <button type="submit">Submit</button> 
     <asp:Repeater ID="Repeater1" runat="server"> 
      <ItemTemplate> 
       <div>Some text</div> 
      </ItemTemplate> 
     </asp:Repeater> 
    </div> 
    </form> 
</body> 
</html> 

然后隐藏代码,你可以做你需要pageLoad的

public partial class JonSkeetForm : System.Web.UI.Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     text1.Value = Request.QueryString[text1.ClientID]; 
     text2.Value = Request.QueryString[text2.ClientID]; 
    } 
} 
一切

如果你不想要一个具有runat="server"的表单,那么你应该使用HTML控件。为您的目的而工作更容易。只需使用普通的HTML标签,并把runat="server"并给他们一个ID。然后,您可以通过编程方式访问它们而不是ViewState

唯一的缺点是,您将无法访问诸如GridView之类的许多“有用的”ASP.NET服务器控件。我在我的例子中包含了一个Repeater,因为我假设你想在结果的同一页上有字段,并且(据我所知)Repeater是唯一的DataBound控件,它将在Form标签中没有runat="server"属性的情况下运行。

1

你有没有想过没有消除POST,而是在表单发布后重定向到合适的GET url。也就是说,接受GET和POST,但是在POST上构建一个GET请求并重定向到它。这可以在页面上处理,也可以通过HttpModule处理,如果你想使其与页面无关。我认为这会让事情变得更容易。

编辑:我假设你在页面上设置了EnableViewState =“false”。

1

我会创建处理路由(类似于MVC,但并不复杂,只是一对夫妇if语句)的HTTP模块并将其交给aspxashx页面。 aspx是首选,因为它更容易修改页面模板。但是,我不会在aspx中使用WebControls。只需Response.Write。顺便说一下,为了简化,您可以在模块中进行参数验证(因为它可能与路由共享代码),并将其保存到HttpContext.Items,然后将它们呈现在页面中。这将非常像MVC没有所有的钟声和口哨声。这是我在ASP.NET MVC时代之前做了很多。

12

你肯定是(恕我直言)在正确的轨道上不使用runat =“服务器”在您的FORM标记。这只是意味着你将需要从直接的Request.QueryString提取值,不过,因为在这个例子中:

在.aspx页面本身:

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="FormPage.aspx.cs" Inherits="FormPage" %> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <title>ASP.NET with GET requests and no viewstate</title> 
</head> 
<body> 
    <asp:Panel ID="ResultsPanel" runat="server"> 
     <h1>Results:</h1> 
     <asp:Literal ID="ResultLiteral" runat="server" /> 
     <hr /> 
    </asp:Panel> 
    <h1>Parameters</h1> 
    <form action="FormPage.aspx" method="get"> 
    <label for="parameter1TextBox"> 
     Parameter 1:</label> 
    <input type="text" name="param1" id="param1TextBox" value='<asp:Literal id="Param1ValueLiteral" runat="server" />'/> 
    <label for="parameter1TextBox"> 
     Parameter 2:</label> 
    <input type="text" name="param2" id="param2TextBox" value='<asp:Literal id="Param2ValueLiteral" runat="server" />'/> 
    <input type="submit" name="verb" value="Submit" /> 
    </form> 
</body> 
</html> 

,并在后台代码:

using System; 

public partial class FormPage : System.Web.UI.Page { 

     private string param1; 
     private string param2; 

     protected void Page_Load(object sender, EventArgs e) { 

      param1 = Request.QueryString["param1"]; 
      param2 = Request.QueryString["param2"]; 

      string result = GetResult(param1, param2); 
      ResultsPanel.Visible = (!String.IsNullOrEmpty(result)); 

      Param1ValueLiteral.Text = Server.HtmlEncode(param1); 
      Param2ValueLiteral.Text = Server.HtmlEncode(param2); 
      ResultLiteral.Text = Server.HtmlEncode(result); 
     } 

     // Do something with parameters and return some result. 
     private string GetResult(string param1, string param2) { 
      if (String.IsNullOrEmpty(param1) && String.IsNullOrEmpty(param2)) return(String.Empty); 
      return (String.Format("You supplied {0} and {1}", param1, param2)); 
     } 
    } 

这里的诀窍是我们在文本输入的value =“”属性中使用了ASP.NET Literals,因此文本框本身不必runat =“server”。然后将结果包装在ASP:Panel中,并在页面加载时设置Visible属性,具体取决于您是否要显示任何结果。

1

我真的很乐意完全放弃页面类,只是处理每个请求与基于url的大开关大小写。 Evey“page”成为一个html模板和一个c#对象。模板类使用带匹配委托的正则表达式来与密钥集合进行比较。

好处:

  1. 它的真快,甚至是重新编译后,几乎没有滞后(页面类必须是大)
  2. 控制确实是颗粒状(伟大的搜索引擎优化,以及各具特色的DOM与JS发挥出色)
  3. 呈现为逻辑
  4. jQuery的单独具有的HTML的总控制

缺点,第:

  1. 简单的东西需要一点在 不再是一个单一的文本框,需要代码 在几个地方,但它确实规模 起来真的很好
  2. 它总是诱人的只是页面视图,直到做我看到一个 viewstate(urgh)然后我回到 现实。

乔恩,星期六早上我们在做什么:)?

1

我认为asp:Repeater控件已经过时了。

的ASP.NET模板引擎是好的,但你可以很轻松地完成了对循环重复...

<form action="JonSkeetForm.aspx" method="get"> 
<div> 
    <input type="text" ID="text1" runat="server" /> 
    <input type="text" ID="text2" runat="server" /> 
    <button type="submit">Submit</button> 
    <% foreach(var item in dataSource) { %> 
     <div>Some text</div> 
    <% } %> 
</div> 
</form> 

ASP.NET窗体是有点好,有从Visual Studio体面的支持,但这runat =“服务器”的事情,这是错误的。 ViewState。

我建议你看看是什么让ASP.NET MVC如此伟大,谁将它从ASP.NET Forms方法移开而不会丢掉所有东西。

您甚至可以编写自己的构建提供程序来编译自定义视图,如NHaml。我认为你应该在这里寻求更多的控制权,并且仅仅依靠ASP。用于包装HTTP和作为CLR托管环境的.NET运行时。如果你运行集成模式,那么你也可以操纵HTTP请求/响应。

2

好了乔恩,视图状态的问题,第一:

,如果有任何一种,因为2.0的内部代码的变化,但这里的如何我处理得到一个几年前摆脱视图状态的我没有检查。实际上,隐藏的字段在HtmlForm内部是硬编码的,所以你应该派生你的新字段,并进入你自己的调用。请注意,您也可以留下__EVENTTARGET和__EVENTTARGET,如果你坚持老式输入控件(我猜你会想,因为它可以帮助无法在客户端上需要JS):

protected override void RenderChildren(System.Web.UI.HtmlTextWriter writer) 
{ 
    System.Web.UI.Page page = this.Page; 
    if (page != null) 
    { 
     onFormRender.Invoke(page, null); 
     writer.Write("<div><input type=\"hidden\" name=\"__eventtarget\" id=\"__eventtarget\" value=\"\" /><input type=\"hidden\" name=\"__eventargument\" id=\"__eventargument\" value=\"\" /></div>"); 
    } 

    ICollection controls = (this.Controls as ICollection); 
    renderChildrenInternal.Invoke(this, new object[] {writer, controls}); 

    if (page != null) 
     onFormPostRender.Invoke(page, null); 
} 

所以,你得到的3静态的MethodInfo的,并呼吁他们出跳过ViewState的一部分了;)

static MethodInfo onFormRender; 
static MethodInfo renderChildrenInternal; 
static MethodInfo onFormPostRender; 

,这里是你的窗体的类型构造:

static Form() 
{ 
    Type aspNetPageType = typeof(System.Web.UI.Page); 

    onFormRender = aspNetPageType.GetMethod("OnFormRender", BindingFlags.Instance | BindingFlags.NonPublic); 
    renderChildrenInternal = typeof(System.Web.UI.Control).GetMethod("RenderChildrenInternal", BindingFlags.Instance | BindingFlags.NonPublic); 
    onFormPostRender = aspNetPageType.GetMethod("OnFormPostRender", BindingFlags.Instance | BindingFlags.NonPublic); 
} 

如果我得到你的问题的权利,Y欧也想不使用POST作为表单的动作所以这里你会怎么做:

protected override void RenderAttributes(System.Web.UI.HtmlTextWriter writer) 
{ 
    writer.WriteAttribute("method", "get"); 
    base.Attributes.Remove("method"); 

    // the rest of it... 
} 

我想这是相当多了。让我知道事情的后续。

编辑:我忘了页面视图状态的方法:

所以,你的自定义表单:HtmlForm控件得到了全新的抽象(或没有)页:System.Web.UI.Page:P

protected override sealed object SaveViewState() 
{ 
    return null; 
} 

protected override sealed void SavePageStateToPersistenceMedium(object state) 
{ 
} 

protected override sealed void LoadViewState(object savedState) 
{ 
} 

protected override sealed object LoadPageStateFromPersistenceMedium() 
{ 
    return null; 
} 

在这种情况下,我会密封方法因为你不能密封页面(即使它不是抽象的Scott Guthrie会将其包装成另一个:P),但是你可以密封你的表单。

相关问题