小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

細(xì)說(shuō) Form (表單)

 賈朋亮博客 2011-07-17

Form(表單)對(duì)于每個(gè)WEB開(kāi)發(fā)人員來(lái)說(shuō),應(yīng)該是再熟悉不過(guò)的東西了,可它卻是頁(yè)面與WEB服務(wù)器交互過(guò)程中最重要的信息來(lái)源。雖然Asp.net WebForms框架為了幫助我們簡(jiǎn)化開(kāi)發(fā)工作,做了很完美的封裝,讓我們只需要簡(jiǎn)單地使用服務(wù)端控件就可以直接操作那些 HTML表單元素了。但我認(rèn)為了解一些基礎(chǔ)的東西,可以使我們不必束縛在WebForms框架上,以及遇到一些奇怪問(wèn)題時(shí),可以更從容地解決它們。

今天,我將和大家來(lái)聊聊表單,這個(gè)簡(jiǎn)單又基礎(chǔ)的東西。我將站在HTML和單純的Asp.net框架的角度來(lái)解釋它們的工作方式,因此,本文不演示W(wǎng)ebForms服務(wù)器控件的相關(guān)內(nèi)容。

簡(jiǎn)單的表單,簡(jiǎn)單的處理方式

好了,讓我們進(jìn)入今天的主題,看看下面這個(gè)簡(jiǎn)單的HTML表單。

<form action="Handler1.ashx" method="post" >
<p>客戶(hù)名稱(chēng): <input type="text" name="CustomerName" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="CustomerTel" style="width: 300px" /></p>
<p><input type="submit" value="提交" /></p>
</form>

在這個(gè)HTML表單中,我定義了二個(gè)文本輸入框,一個(gè)提交按鈕,表單將提交到Handler1.ashx中處理,且以POST的方式。
注意哦,如果我們想讓純靜態(tài)頁(yè)面也能向服務(wù)器提交數(shù)據(jù),就可以采用這樣方式來(lái)處理:將action屬性指向一個(gè)服務(wù)器能處理的地址。

說(shuō)明:當(dāng)我們使用WebForms的服務(wù)器表單控件時(shí),一般都會(huì)提交到頁(yè)面自身來(lái)處理(action屬性指向當(dāng)前頁(yè)面),這樣可以方便地使用按鈕事件以及從服務(wù)器控件訪(fǎng)問(wèn)從瀏覽器提交的控件輸入結(jié)果。
如果在URL重寫(xiě)時(shí),希望能在頁(yè)面回傳時(shí)保持URL不變,即:action為重寫(xiě)后的URL,那么可以Page類(lèi)中執(zhí)行以下調(diào)用:

Form.Action = Request.RawUrl;    // 受以下版本支持:3.5 SP1、3.0 SP1、2.0 SP1

好了,我們?cè)倩氐角懊婺莻€(gè)HTML表單,看一下如果用戶(hù)點(diǎn)擊了“提交”按鈕,瀏覽器是如何把表單的內(nèi)容發(fā)出的。在此,我們需要Fiddler工具的協(xié)助,請(qǐng)?jiān)谔峤槐韱吻皢?dòng)好Fiddler。我將這個(gè)表單的提交請(qǐng)求過(guò)程做了如下截圖。

上圖是將要提交的表單的輸入情況,下圖是用Fiddler看到的瀏覽器發(fā)出的請(qǐng)求內(nèi)容。

在這張圖片中,我們可以看到瀏覽器確實(shí)將請(qǐng)求發(fā)給了我前面在action中指定的地址,且以POST形式發(fā)出的。表單的二個(gè)控件的輸入值放在請(qǐng)求體中,且做了【編碼】處理,編碼的方式用請(qǐng)求頭【Content-Type】說(shuō)明,這樣,當(dāng)服務(wù)端收到請(qǐng)求后,就知道該如何讀取請(qǐng)求的內(nèi)容了。注意:表單的數(shù)據(jù)是以name1=value1&name2=value2 的形式提交的,其中name,value分別對(duì)應(yīng)了表單控件的相應(yīng)屬性。

我們還可以在Fiddler中,將視圖切換到WebForms選項(xiàng)卡,這樣能更清楚地只查看瀏覽器提交的數(shù)據(jù),如下圖。

看了客戶(hù)端的頁(yè)面和請(qǐng)求的內(nèi)容,我們?cè)賮?lái)看看在服務(wù)端如何獲取瀏覽器提交的表單的輸入吧,代碼如下:

string name = context.Request.Form["CustomerName"];
string tel = context.Request.Form["CustomerTel"];

代碼很簡(jiǎn)單,直接根據(jù)表單控件的name屬性訪(fǎng)問(wèn)Request.Form就可以了。

表單提交,成功控件

我們?cè)賮?lái)看一下瀏覽器是如何提交表單的,或者說(shuō),瀏覽器在提交表單時(shí),要做哪些事情。

瀏覽器并不是將所有的表單控件全部發(fā)送到服務(wù)器的,而是會(huì)查找所有的【成功控件】,只將這些成功控件的數(shù)據(jù)發(fā)送到服務(wù)端,什么是成功控件呢?
簡(jiǎn)單地來(lái)說(shuō),成功控件就是:每個(gè)表單中的控件都應(yīng)該有一個(gè)name屬性和”當(dāng)前值“,在提交時(shí),它們將以 name=value 的形式做為提交數(shù)據(jù)的一部分。
對(duì)于一些特殊情況,成功控件還有以下規(guī)定:
1. 控件不能是【禁用】狀態(tài),即指定【disabled="disabled"】。即:禁用的控件將不是成功控件。
2. 如果一個(gè)表單包含了多個(gè)提交按鍵,那么僅當(dāng)用戶(hù)點(diǎn)擊的那個(gè)提交按鈕才算是成功控件。
3. 對(duì)于checkbox控件來(lái)說(shuō),只有被用戶(hù)勾選的才算是成功控件。
4. 對(duì)于radio button來(lái)說(shuō),只有被用戶(hù)勾選的才算是成功控件。
5. 對(duì)于select控件來(lái)說(shuō),所有被選擇的選項(xiàng)都做為成功控件,name由select控件提供。
6. 對(duì)于file上傳文件控件來(lái)說(shuō),如果它包含了選擇的文件,那么它將是一個(gè)成功控件。
此外,瀏覽器不會(huì)考慮Reset按鈕以及OBJECT元素。

注意:
1. 對(duì)于checkbox, radio button來(lái)說(shuō),如果它們被確認(rèn)為成功控件,但沒(méi)有為控件指定value屬性,那么在表單提交時(shí),將會(huì)以"on"做為它們的value
2. 如果在服務(wù)端讀不到某個(gè)表單控件的值,請(qǐng)檢查它是否滿(mǎn)足以上規(guī)則。

提交方式:在前面的示例代碼中,我為form指定了method="post",這個(gè)提交方法就決定了瀏覽器在提交數(shù)據(jù)時(shí),通過(guò)什么方式來(lái)傳遞它們。
如果是【post】,那么表單數(shù)據(jù)將放在請(qǐng)求體中被發(fā)送出去。
如果是【get】,那么表單數(shù)據(jù)將會(huì)追加到查詢(xún)字符串中,以查詢(xún)字符串的形式提交到服務(wù)端。
建議:表單通常還是以post方式提交比較好,這樣可以不破壞URL,況且URL還有長(zhǎng)度限制。

數(shù)據(jù)的編碼:前面我將瀏覽器的請(qǐng)求細(xì)節(jié)用Fiddler做了個(gè)截圖,從這個(gè)圖中我們可以看到:控件輸入的內(nèi)容并不是直接發(fā)送的,而是經(jīng)過(guò)一種編碼規(guī)則來(lái)處理的。目前基本上只會(huì)只使用二種編碼規(guī)則:application/x-www-form-urlencoded 和 multipart/form-data ,這二個(gè)規(guī)則的使用場(chǎng)景簡(jiǎn)單地說(shuō)就是:后者在上傳文件時(shí)使用,其它情形則使用前者(默認(rèn))。
這個(gè)規(guī)則是在哪里指定的呢? 其實(shí)form還有個(gè)enctype屬性,用它就可以指定編碼規(guī)則,當(dāng)我在VS2008寫(xiě)代碼時(shí),會(huì)有以下提示:

按照我前面說(shuō)過(guò)的編碼規(guī)則選擇邏輯,application/x-www-form-urlencoded做為默認(rèn)值,所以,一般情況下我們并不用顯式指定。除非我們要上傳文件了,那么此時(shí)必須設(shè)置enctype="multipart/form-data"

好了,說(shuō)了這么一大堆理論,我們?cè)賮?lái)看一下瀏覽是如何處理表單數(shù)據(jù)的。這個(gè)過(guò)程大致分為4個(gè)階段:
1. 識(shí)別所有的成功控件。
2. 為所有的成功控件創(chuàng)建一個(gè)數(shù)據(jù)集合,它們包含 control-name/current-value 這樣的值對(duì)。
3. 按照f(shuō)orm.enctype指定的編碼規(guī)則對(duì)前面準(zhǔn)備好的數(shù)據(jù)進(jìn)行編碼。編碼規(guī)則將放在請(qǐng)求中,用【Content-Type】指出。
4. 提交編碼后的數(shù)據(jù)。此時(shí)會(huì)區(qū)分post,get二種情況,提交的地址由form.action屬性指定的。

多提交按鈕的表單

用過(guò)Asp.net WebForms框架的人可能都寫(xiě)過(guò)這樣的頁(yè)面:一個(gè)頁(yè)面中包含多個(gè)服務(wù)端按鈕。處理方式嘛,也很簡(jiǎn)單:在每個(gè)按鈕的事件處理器寫(xiě)上相應(yīng)的代碼就完事了,根本不用我們想太多。
不過(guò),對(duì)于不理解這背后處理過(guò)程的開(kāi)發(fā)人員來(lái)說(shuō),當(dāng)他們轉(zhuǎn)到MVC框架下,可能會(huì)被卡住:MVC框架中可沒(méi)有按鈕事件!即使用不用MVC框架,用ashx通用處理器的方式,也會(huì)遇到這種問(wèn)題,怎么辦?
對(duì)于這個(gè)問(wèn)題,本文將站在HTML角度給出二個(gè)最根本的解決辦法。

方法1:根據(jù)【成功控件】定義,我們?cè)O(shè)置按鈕的name,在服務(wù)端用name來(lái)區(qū)分哪個(gè)按鈕的提交:

HTML代碼

<form action="Handler1.ashx" method="post">
<p>客戶(hù)名稱(chēng): <input type="text" name="CustomerName" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="CustomerTel" style="width: 300px" /></p>
<p><input type="submit" name="btnSave" value="保存" />
<input type="submit" name="btnQuery" value="查詢(xún)" />
</p>
</form>

服務(wù)端處理代碼

// 注意:我們只要判斷指定的name是否存在就可以了。
if( string.IsNullOrEmpty(context.Request.Form["btnSave"]) == false ) {
// 保存的處理邏輯
}
if( string.IsNullOrEmpty(context.Request.Form["btnQuery"]) == false ) {
// 查詢(xún)的處理邏輯
}

方法2:我將二個(gè)按鈕的name設(shè)置為相同的值(根據(jù)前面的成功控件規(guī)則,只有被點(diǎn)擊的按鈕才會(huì)提交),在服務(wù)端判斷value,示例代碼如下:

<form action="Handler1.ashx" method="post">
<p>客戶(hù)名稱(chēng): <input type="text" name="CustomerName" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="CustomerTel" style="width: 300px" /></p>
<p><input type="submit" name="submit" value="保存" />
<input type="submit" name="submit" value="查詢(xún)" />
</p>
</form>

string action = context.Request.Form["submit"];
if( action == "保存" ) {
// 保存的處理邏輯
}
if( action == "查詢(xún)" ) {
// 查詢(xún)的處理邏輯
}

當(dāng)然了,解決這個(gè)問(wèn)題的方法很多,我們還可以在提交前修改form.action屬性。對(duì)于MVC來(lái)說(shuō),可能有些人會(huì)選擇使用Filter的方式來(lái)處理。最終選擇哪種方法,可根據(jù)各自喜好來(lái)選擇。
我可能更喜歡直接使用Ajax提交到一個(gè)具體的URL,這樣也很直觀,在服務(wù)端也就不用這些判斷了。接著往下看吧。

上傳文件的表單

前面我說(shuō)到“數(shù)據(jù)的編碼"提到了form.enctype,這個(gè)屬性正是上傳表單與普通表單的區(qū)別,請(qǐng)看以下示例代碼:

<form action="Handler2.ashx" method="post" enctype="multipart/form-data">
<p><input type="text" name="str" value="一個(gè)字符串,別管它" /></p>
<p>要上傳的文件1<input type="file" name="file1"/></p>
<p>要上傳的文件2<input type="file" name="file2"/></p>
<p><input type="submit" value="提交" /></p>
</form>

我將上傳2個(gè)小文件

我們?cè)賮?lái)看看當(dāng)我點(diǎn)擊提交按鈕時(shí),瀏覽器發(fā)送的請(qǐng)求是個(gè)什么樣子的:

注意我用紅色邊框框出來(lái)的部分,以及請(qǐng)求體中的內(nèi)容。此時(shí)請(qǐng)求頭Content-Type的值發(fā)生了改變,而且還多了一個(gè)叫boundary的參數(shù),它將告訴服務(wù)端:請(qǐng)求體的內(nèi)容以這個(gè)標(biāo)記來(lái)分開(kāi)。 并且,請(qǐng)求體中每個(gè)分隔標(biāo)記會(huì)單獨(dú)占一行,且具體內(nèi)容為:"--" + boundary,最后結(jié)束的分隔符的內(nèi)容為:"--" + boundary + "--" 也是獨(dú)占一行。從圖片中我們還可以發(fā)現(xiàn),在請(qǐng)求體的每段數(shù)據(jù)前,還有一塊描述信息。
具體這些內(nèi)容是如何生成的,可以參考本文后面的實(shí)現(xiàn)代碼。

再來(lái)看看在服務(wù)端如何讀取上傳的文件。

HttpPostedFile file1 = context.Request.Files["file1"];
if( file1 != null && string.IsNullOrEmpty(file1.FileName) == false )
file1.SaveAs(context.Server.MapPath("~/App_Data/") + file1.FileName);
HttpPostedFile file2 = context.Request.Files["file2"];
if( file2 != null && string.IsNullOrEmpty(file2.FileName) == false )
file2.SaveAs(context.Server.MapPath("~/App_Data/") + file2.FileName);

或者

HttpFileCollection files = context.Request.Files;
foreach( string key in files.AllKeys ) {
HttpPostedFile file = files[key];
if( string.IsNullOrEmpty(file.FileName) == false )
file.SaveAs(context.Server.MapPath("~/App_Data/") + file.FileName);
}

二種方法都行,前者更能體現(xiàn)控件的name與服務(wù)端讀取的關(guān)系,后者在多文件上傳時(shí)有更好的擴(kuò)展性。

安全問(wèn)題:注意,上面示例代碼中,這樣的寫(xiě)法是極不安全的。正確的做法應(yīng)該是:重新生成一個(gè)隨機(jī)的文件名,而且最好能對(duì)文件內(nèi)容檢查,例如,如果是圖片,可以調(diào)用.net的一些圖形類(lèi)打開(kāi)文件,然后"另存"文件??傊?,在安全問(wèn)題面前只有一個(gè)原則:不要相信用戶(hù)的輸入,一定要檢查或者轉(zhuǎn)換。

MVC Controller中多個(gè)自定義類(lèi)型的傳入?yún)?shù)

前面的所有示例代碼中都有一個(gè)規(guī)律:在服務(wù)端讀取瀏覽器提交的數(shù)據(jù)時(shí),都會(huì)使用控件的name屬性,基本上在Asp.net中就是這樣處理。但是在MVC中,MS為了簡(jiǎn)化讀取表單數(shù)據(jù)的代碼,可以讓我們直接在Controller的方法中直接以傳入?yún)?shù)的形式指定,此時(shí)框架會(huì)自動(dòng)根據(jù)方法的參數(shù)名查找對(duì)應(yīng)的輸入數(shù)據(jù)(當(dāng)然也不止表單數(shù)據(jù)了)。下面舉個(gè)簡(jiǎn)單的例子:

<form action="/Home/Submit" method="post">
<p>客戶(hù)名稱(chēng): <input type="text" name="Name" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="Tel" style="width: 300px" /></p>
<p><input type="submit" value="提交" /></p>
</form>

Conntroller中的方法的簽名:

public ActionResult Submit(Customer customer)
{
}
public ActionResult Submit(string name, string tel)
{
}

以上二種方法都是可以的,當(dāng)然了,前者會(huì)比較好,但需要事先定義一個(gè)Customer類(lèi),代碼如下:

public class Customer
{
public string Name { get; set; }
public string Tel { get; set; }
}

如果表單簡(jiǎn)單或者業(yè)務(wù)邏輯簡(jiǎn)單,我們或許一直也不會(huì)遇到什么麻煩,以上代碼能很好的工作。但是,如果哪天我們有了新的業(yè)務(wù)需要求,需要在這個(gè)表單中同時(shí)加上一些其它的內(nèi)容,例如,要把業(yè)務(wù)員的資料也一起錄入進(jìn)去。其中業(yè)務(wù)員的實(shí)體類(lèi)定義如下:

public class Salesman
{
public string Name { get; set; }
public string Tel { get; set; }
}

Controller的接口需要修改成:

public ActionResult Submit(Customer customer, Salesman salesman)
{
}

這時(shí),HTML表單又該怎么寫(xiě)呢?剛好,這二個(gè)類(lèi)的(部分)屬性名稱(chēng)一樣,顯然,前面表單中的Name,Tel就無(wú)法對(duì)應(yīng)了。此時(shí)我們可以將表單寫(xiě)成如下形式:

<form action="/Home/Submit" method="post">
<p>客戶(hù)名稱(chēng): <input type="text" name="customer.Name" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="customer.Tel" style="width: 300px" /></p>
<p>銷(xiāo)售員名稱(chēng): <input type="text" name="salesman.Name" style="width: 300px" /></p>
<p>銷(xiāo)售員電話(huà): <input type="text" name="salesman.Tel" style="width: 300px" /></p>
<p><input type="submit" value="提交" /></p>
</form>

注意Controller方法中的參數(shù)名與HTML表單中的name是有關(guān)系的。

F5刷新問(wèn)題并不是WebForms的錯(cuò)

剛才說(shuō)到了MVC框架,再來(lái)說(shuō)說(shuō)WebForms框架。以前時(shí)常聽(tīng)到有些人在抱怨用WebForms的表單有F5的刷新重復(fù)提交問(wèn)題。在此我想為WebForms說(shuō)句公道話(huà):這個(gè)問(wèn)題并不是WebForms本身的問(wèn)題,是瀏覽器的問(wèn)題,只是如果您一直使用WebForms的較傳統(tǒng)用法,是容易產(chǎn)生這個(gè)現(xiàn)象的。那么什么叫做【傳統(tǒng)用法】呢?這里我就給個(gè)我自己的定義吧:所謂的WebForms的傳統(tǒng)用法是說(shuō):您的頁(yè)面一直使用服務(wù)器控件的提交方式(postback),在事件處理后,頁(yè)面又進(jìn)入再一次的重現(xiàn)過(guò)程,或者說(shuō):當(dāng)前頁(yè)面一直在使用POST方式向當(dāng)前頁(yè)面提交。

那么如何避開(kāi)這個(gè)問(wèn)題呢?辦法大致有2種:

1. PRG模式(Post-Redirect-Get),在事件處理后,調(diào)用重定向的操作Response.Redirect(),而不要在事件處理的后期再去給一些服務(wù)器控件綁定數(shù)據(jù)項(xiàng)了!
建議:按鈕事件只做一些提交數(shù)據(jù)的處理,將數(shù)據(jù)綁定的操作放在OnPreRender方法中處理,而不是寫(xiě)在每個(gè)事件中(遍地開(kāi)花)。不過(guò),這種方式下,可能偉大的ViewState就發(fā)揮不了太大的作用了,如果您發(fā)現(xiàn)ViewState沒(méi)用了,在Web.config中全局關(guān)掉后,又發(fā)現(xiàn)很多服務(wù)器控件的高級(jí)事件又不能用了!嗯,杯具有啊。
這個(gè)話(huà)題說(shuō)下去又沒(méi)完沒(méi)了,到此為止吧,不過(guò),千萬(wàn)不要以為這種方法是在倒退哦。

2. 以Ajax方式提交表單,請(qǐng)繼續(xù)閱讀本文。

以Ajax方式提交整個(gè)表單

前面一直在說(shuō)”瀏覽器提交表單",事實(shí)上我們也可以用JavaScript提交表單,好處也有很多,比如前面所說(shuō)的F5刷新問(wèn)題。以Ajax方式提交表單的更大好處它是異步的,還可以實(shí)現(xiàn)局部刷新,這些特性都是瀏覽器提交方式?jīng)]有的。前面我提到表單在提交時(shí),瀏覽器要實(shí)現(xiàn)的4個(gè)步驟,基本上用JS來(lái)完成這個(gè)操作也是一樣的。但是,前面說(shuō)的步驟好像很麻煩呢,有沒(méi)有簡(jiǎn)單的方法來(lái)實(shí)現(xiàn)這個(gè)過(guò)程呢?嗯,有的,這里我將使用JQuery以及jquery.form.js這個(gè)插件來(lái)演示這個(gè)復(fù)雜過(guò)程的簡(jiǎn)單處理方案。

示例用的HTML表單還是我前面用的代碼,完全不需要修改:

<form action="Handler1.ashx" method="post" >
<p>客戶(hù)名稱(chēng): <input type="text" name="CustomerName" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="CustomerTel" style="width: 300px" /></p>
<p><input type="submit" value="提交" /></p>
</form>

JS代碼如下:

$(function(){
$('form').ajaxForm({
success: function(responseText){
alert(responseText);
}
});
});

是的,就是這么簡(jiǎn)單,只要調(diào)用ajaxForm()就行了。你也可以傳入任何$.ajax()能接受的參數(shù)。
它的作用是:修改表單的提交方式,改成Ajax方式提交。最終當(dāng)用戶(hù)點(diǎn)擊“提交”按鈕時(shí),此時(shí)不再是瀏覽器的提交行為了,而是使用Ajax的方式提交,提交的URL以及提交方法就是在FORM中指定的參數(shù)。

如果您希望要用戶(hù)點(diǎn)擊某個(gè)按鈕或者鏈接時(shí),也能提交表單(不經(jīng)過(guò)提交按鈕),那么可以使用如下方法:

$(function(){
$("#btnId").click(function(){
$('form').ajaxSubmit({
success: function(responseText){
alert(responseText);
}
});
});
});

變化很小,只需要將ajaxForm修改成ajaxSubmit就OK了。 與ajaxForm()不同,調(diào)用ajaxSubmit()方法將會(huì)立即提交表單。

以Ajax方式提交部分表單

在前面的示例中,我們看到以Ajax方式提交一個(gè)表單是非常容易的,它完全模擬了瀏覽器的行為。不過(guò),有時(shí)我們可能需要只提交表單的一部分,為的是更好的局部更新,那么又該如何做呢?
假如我有以下表單的一部分,我只希望在用戶(hù)某個(gè)按鈕時(shí)將它提交到服務(wù)端:

<div id="divCustomerInfo">
<p>客戶(hù)名稱(chēng): <input type="text" name="CustomerName" style="width: 300px" /></p>
<p>客戶(hù)電話(huà): <input type="text" name="CustomerTel" style="width: 300px" /></p>
</div>

我們可以這樣來(lái)提交這部分表單的數(shù)據(jù):

$("#btnId").click(function(){
$.ajax({
url: "Handler1.ashx", type: "POST",
data: $('#divCustomerInfo :text').fieldSerialize(),
success: function(responseText){
alert(responseText);
}
});
return false;
});

注意關(guān)鍵的代碼行:data: $('#divCustomerInfo :text').fieldSerialize()
注意:此時(shí)將由您指定一個(gè)【JQuery選擇器】來(lái)過(guò)濾要提交的控件,而不是使用成功控件的篩選邏輯。

或者,您也可以使用下面將要介紹的方法,仍然是使用 data: {} 的方式,但需要手工指定數(shù)據(jù)成員。

使用JQuery,就不要再拼URL了!

JQuery越來(lái)越流行,以至于在創(chuàng)建MVC項(xiàng)目時(shí),VS IDE會(huì)把JQuery也準(zhǔn)備好了,可能MS認(rèn)為開(kāi)發(fā)WEB項(xiàng)目離不開(kāi)JQuery了。
的確,JQuery非常方便,尤其是在處理DOM時(shí),不僅如此,在處理AJAX請(qǐng)求時(shí),也非常方便。

不過(guò),有件事卻讓我很納悶:經(jīng)常看到有人在使用JQuery實(shí)現(xiàn)Ajax時(shí),把一堆參數(shù)放在URL中傳遞,當(dāng)然了,發(fā)送GET請(qǐng)求嘛,這樣做不錯(cuò),但是,讓我不解的是:URL是拼接起來(lái)的,而且代碼又臭又長(zhǎng)!

如果是一個(gè)簡(jiǎn)單的參數(shù):"aaa.aspx?id=" + xxId ,這樣也就罷了。但是當(dāng)一堆參數(shù)拼接在一起時(shí),可能一下子還看不清楚到底有幾個(gè)什么樣的參數(shù)。而且經(jīng)驗(yàn)豐富一些的開(kāi)發(fā)人員會(huì)發(fā)現(xiàn)這樣做有時(shí)會(huì)有亂碼問(wèn)題,可能網(wǎng)上搜過(guò)后,知道還有編碼的工作要處理,于是又加了一堆編碼方法。到此為止,這段代碼會(huì)讓人看起來(lái)很累!

如果您平時(shí)也是這樣做的,那么我今天就告訴您:不要再拼接URL了! $.ajax()的參數(shù)不是有個(gè)data成員嘛,用它吧??创a:

$.ajax({
url: "Handler1.ashx", type: "POST",
data: { id: 2, name: "aaa", tel: "~!@#$%^&*()_+-=<>?|", xxxx: "要多少還可以寫(xiě)多少", encoding: "見(jiàn)鬼去吧。?& :)" },
success: function(responseText) {
$("#divResult").html(responseText);
}
});

你說(shuō)什么,只能使用GET ? 哦,那就改一下 type 參數(shù)吧。

$.ajax({
url: "Handler1.ashx", type: "GET",
data: { id: 2, name: "aaa", tel: "~!@#$%^&*()_+-=<>?|", xxxx: "要多少還可以寫(xiě)多少", encoding: "見(jiàn)鬼去吧。?& :)" },
success: function(responseText) {
$("#divResult").html(responseText);
}
});

看了這個(gè)示例,您還會(huì)繼續(xù)拼URL嗎?

說(shuō)明:為了排版簡(jiǎn)單,我將參數(shù)放在一行了,建議實(shí)際使用時(shí),不要擠在一行。

id, name 有什么關(guān)系

通常我們?cè)趯?xiě)HTML代碼時(shí),會(huì)給控件指定一個(gè)id屬性,這個(gè)屬性只供JS和CSS使用,在表單提交時(shí),它不起任何作用。

在上面的示例代碼中,可能data {}中的各個(gè)value就來(lái)源于各個(gè)不同的控件,那么為那些控件指定相應(yīng)的id屬性將會(huì)方便地找到它們。
但是如果不需要用JS和CSS控制的控件,或許它們只是用來(lái)顯示一些數(shù)據(jù)(只讀),那么就沒(méi)有必要指定id屬性,當(dāng)然了,name屬性也可以不用給出(避免提交無(wú)意義的數(shù)據(jù))。

使用C#模擬瀏覽器提交表單

瀏覽器也是一個(gè)普通的應(yīng)用程序,.net framework也提供一些類(lèi)也能讓我們直接發(fā)起HTTP請(qǐng)求。今天我將再次用C#來(lái)模擬瀏覽器的提交請(qǐng)求,同時(shí)也可以加深對(duì)HTTP請(qǐng)求的理解。

示例代碼分為二段,一段示范了使用application/x-www-form-urlencoded編碼方式提交,另一段則示范了使用multipart/form-data的編碼方式。
為了讓大家能再次利用這些代碼,我已將關(guān)鍵部分寫(xiě)成獨(dú)立方法,希望當(dāng)您有這方面的需求時(shí)能馬上可以用上。代碼如下:

1. application/x-www-form-urlencoded

/// <summary>
/// 向指定的URL地址發(fā)起一個(gè)POST請(qǐng)求,同時(shí)可以上傳一些數(shù)據(jù)項(xiàng)。
/// </summary>
/// <param name="url">要請(qǐng)求的URL地址</param>
/// <param name="keyvalues">要上傳的數(shù)據(jù)項(xiàng)</param>
/// <param name="encoding">發(fā)送,接收的字符編碼方式</param>
/// <returns>服務(wù)器的返回結(jié)果</returns>
static string SendHttpRequestPost(string url, Dictionary<string, string> keyvalues, Encoding encoding)
{
if( string.IsNullOrEmpty(url) )
throw new ArgumentNullException("url");
string postData = null;
// 將數(shù)據(jù)項(xiàng)轉(zhuǎn)變成 name1=value1&name2=value2 的形式
if( keyvalues != null && keyvalues.Count > 0 ) {
postData = string.Join("&",
(from kvp in keyvalues
let item = kvp.Key + "=" + HttpUtility.UrlEncode(kvp.Value)
select item
).ToArray()
);
}
if( encoding == null )
encoding = Encoding.UTF8;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded; charset=" + encoding.WebName;
if( postData != null ) {
byte[] buffer = encoding.GetBytes(postData);
Stream stream = request.GetRequestStream();
stream.Write(buffer, 0, buffer.Length);
stream.Close();
}
using( WebResponse response = request.GetResponse() ) {
using( StreamReader reader = new StreamReader(response.GetResponseStream(), encoding) ) {
return reader.ReadToEnd();
}
}
}
// 調(diào)用上面方法的示例代碼
string Test_SendHttpRequestPost()
{
string url = "http://localhost:1272/FormWebSite1/Handler1.ashx";
Dictionary<string, string> keyvalues = new Dictionary<string, string>();
keyvalues.Add("CustomerName", "我是李奇峰,$%@+& ?#^/");
keyvalues.Add("CustomerTel", "1381723505x");
return SendHttpRequestPost(url, keyvalues, null);
}

 

 

2. multipart/form-data 。注意這部分代碼有點(diǎn)復(fù)雜,因此我加了很多注釋。

/// <summary>
/// 向指定的URL地址發(fā)起一個(gè)POST請(qǐng)求,同時(shí)可以上傳一些數(shù)據(jù)項(xiàng)以及上傳文件。
/// </summary>
/// <param name="url">要請(qǐng)求的URL地址</param>
/// <param name="keyvalues">要上傳的數(shù)據(jù)項(xiàng)</param>
/// <param name="fileList">要上傳的文件列表</param>
/// <param name="encoding">發(fā)送數(shù)據(jù)項(xiàng),接收的字符編碼方式</param>
/// <returns>服務(wù)器的返回結(jié)果</returns>
static string SendHttpRequestPost(string url, Dictionary<string, string> keyvalues,
Dictionary<string, string> fileList, Encoding encoding)
{
if( fileList == null )
return SendHttpRequestPost(url, keyvalues, encoding);
if( string.IsNullOrEmpty(url) )
throw new ArgumentNullException("url");
if( encoding == null )
encoding = Encoding.UTF8;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";        // 要上傳文件,一定要是POST方法
// 數(shù)據(jù)塊的分隔標(biāo)記,用于設(shè)置請(qǐng)求頭,注意:這個(gè)地方最好不要使用漢字。
string boundary = "---------------------------" + Guid.NewGuid().ToString("N");
// 數(shù)據(jù)塊的分隔標(biāo)記,用于寫(xiě)入請(qǐng)求體。
//   注意:前面多了一段: "--" ,而且它們將獨(dú)占一行。
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
// 設(shè)置請(qǐng)求頭。指示是一個(gè)上傳表單,以及各數(shù)據(jù)塊的分隔標(biāo)記。
request.ContentType = "multipart/form-data; boundary=" + boundary;
// 先得到請(qǐng)求流,準(zhǔn)備寫(xiě)入數(shù)據(jù)。
Stream stream = request.GetRequestStream();
if( keyvalues != null && keyvalues.Count > 0 ) {
// 寫(xiě)入非文件的keyvalues部分
foreach( KeyValuePair<string, string> kvp in keyvalues ) {
// 寫(xiě)入數(shù)據(jù)塊的分隔標(biāo)記
stream.Write(boundaryBytes, 0, boundaryBytes.Length);
// 寫(xiě)入數(shù)據(jù)項(xiàng)描述,這里的Value部分可以不用URL編碼
string str = string.Format(
"Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}",
kvp.Key, kvp.Value);
byte[] data = encoding.GetBytes(str);
stream.Write(data, 0, data.Length);
}
}
// 寫(xiě)入要上傳的文件
foreach( KeyValuePair<string, string> kvp in fileList ) {
// 寫(xiě)入數(shù)據(jù)塊的分隔標(biāo)記
stream.Write(boundaryBytes, 0, boundaryBytes.Length);
// 寫(xiě)入文件描述,這里設(shè)置一個(gè)通用的類(lèi)型描述:application/octet-stream,具體的描述在注冊(cè)表里有。
string description = string.Format(
"Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
"Content-Type: application/octet-stream\r\n\r\n",
kvp.Key, Path.GetFileName(kvp.Value));
// 注意:這里如果不使用UTF-8,對(duì)于漢字會(huì)有亂碼。
byte[] header = Encoding.UTF8.GetBytes(description);
stream.Write(header, 0, header.Length);
// 寫(xiě)入文件內(nèi)容
byte[] body = File.ReadAllBytes(kvp.Value);
stream.Write(body, 0, body.Length);
}
// 寫(xiě)入結(jié)束標(biāo)記
boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
stream.Write(boundaryBytes, 0, boundaryBytes.Length);
stream.Close();
// 開(kāi)始發(fā)起請(qǐng)求,并獲取服務(wù)器返回的結(jié)果。
using( WebResponse response = request.GetResponse() ) {
using( StreamReader reader = new StreamReader(response.GetResponseStream(), encoding) ) {
return reader.ReadToEnd();
}
}
}
// 調(diào)用上面方法的示例代碼
string Test_SendHttpRequestPost2()
{
string url = "http://localhost:1272/FormWebSite1/Handler2.ashx";
Dictionary<string, string> keyvalues = new Dictionary<string, string>();
keyvalues.Add("Key1", "本示例代碼由 Fish Li 提供");
keyvalues.Add("Key2", "http://www.cnblogs.com/fish-li");
keyvalues.Add("Key3", "來(lái)幾個(gè)特殊字符:~!@#$%^&*()-=_+{}[]:;'\"<>?/.,|\\");
Dictionary<string, string> fileList = new Dictionary<string, string>();
fileList.Add("file1", @"H:\AllTempFiles\ascx中文字.gif");
fileList.Add("file2", @"H:\AllTempFiles\asax中文字.gif");
return SendHttpRequestPost(url, keyvalues, fileList, Encoding.UTF8);
}

說(shuō)明:上面的示例方法中,我并沒(méi)有對(duì)KEY編碼,是因?yàn)椋何蚁氪蠹疫x用的KEY應(yīng)該是不需要編碼的(英文字母與數(shù)字的組合)。
而且,我也沒(méi)加入對(duì)Cookie處理的那部分代碼,如果您需要在發(fā)送請(qǐng)求時(shí),保留Cookie,那么請(qǐng)參考我上一篇博客 【細(xì)說(shuō)Cookie】中的示例代碼。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多