2009-01-10 12 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),但是你可以密封你的表單。

相關問題