乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      細(xì)說 Form (表單)

       賈朋亮博客 2011-07-17

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

      今天,我將和大家來聊聊表單,這個(gè)簡(jiǎn)單又基礎(chǔ)的東西。我將站在HTML和單純的Asp.net框架的角度來解釋它們的工作方式,因此,本文不演示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>客戶名稱: <input type="text" name="CustomerName" style="width: 300px" /></p>
      <p>客戶電話: <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)頁面也能向服務(wù)器提交數(shù)據(jù),就可以采用這樣方式來處理:將action屬性指向一個(gè)服務(wù)器能處理的地址。

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

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

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

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

      在這張圖片中,我們可以看到瀏覽器確實(shí)將請(qǐng)求發(fā)給了我前面在action中指定的地址,且以POST形式發(fā)出的。表單的二個(gè)控件的輸入值放在請(qǐng)求體中,且做了【編碼】處理,編碼的方式用請(qǐng)求頭【Content-Type】說明,這樣,當(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ù),如下圖。

      看了客戶端的頁面和請(qǐng)求的內(nèi)容,我們?cè)賮砜纯丛诜?wù)端如何獲取瀏覽器提交的表單的輸入吧,代碼如下:

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

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

      表單提交,成功控件

      我們?cè)賮砜匆幌聻g覽器是如何提交表單的,或者說,瀏覽器在提交表單時(shí),要做哪些事情。

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

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

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

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

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

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

      多提交按鈕的表單

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

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

      HTML代碼

      <form action="Handler1.ashx" method="post">
      <p>客戶名稱: <input type="text" name="CustomerName" style="width: 300px" /></p>
      <p>客戶電話: <input type="text" name="CustomerTel" style="width: 300px" /></p>
      <p><input type="submit" name="btnSave" value="保存" />
      <input type="submit" name="btnQuery" value="查詢" />
      </p>
      </form>
      

      服務(wù)端處理代碼

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

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

      <form action="Handler1.ashx" method="post">
      <p>客戶名稱: <input type="text" name="CustomerName" style="width: 300px" /></p>
      <p>客戶電話: <input type="text" name="CustomerTel" style="width: 300px" /></p>
      <p><input type="submit" name="submit" value="保存" />
      <input type="submit" name="submit" value="查詢" />
      </p>
      </form>
      

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

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

      上傳文件的表單

      前面我說到“數(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è)賮砜纯串?dāng)我點(diǎn)擊提交按鈕時(shí),瀏覽器發(fā)送的請(qǐng)求是個(gè)什么樣子的:

      注意我用紅色邊框框出來的部分,以及請(qǐng)求體中的內(nèi)容。此時(shí)請(qǐng)求頭Content-Type的值發(fā)生了改變,而且還多了一個(gè)叫boundary的參數(shù),它將告訴服務(wù)端:請(qǐng)求體的內(nèi)容以這個(gè)標(biāo)記來分開。 并且,請(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)代碼。

      再來看看在服務(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ò)展性。

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

      MVC Controller中多個(gè)自定義類型的傳入?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>客戶名稱: <input type="text" name="Name" style="width: 300px" /></p>
      <p>客戶電話: <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類,代碼如下:

      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í)體類定義如下:

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

      Controller的接口需要修改成:

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

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

      <form action="/Home/Submit" method="post">
      <p>客戶名稱: <input type="text" name="customer.Name" style="width: 300px" /></p>
      <p>客戶電話: <input type="text" name="customer.Tel" style="width: 300px" /></p>
      <p>銷售員名稱: <input type="text" name="salesman.Name" style="width: 300px" /></p>
      <p>銷售員電話: <input type="text" name="salesman.Tel" style="width: 300px" /></p>
      <p><input type="submit" value="提交" /></p>
      </form>
      

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

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

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

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

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

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

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

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

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

      <form action="Handler1.ashx" method="post" >
      <p>客戶名稱: <input type="text" name="CustomerName" style="width: 300px" /></p>
      <p>客戶電話: <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)用戶點(diǎn)擊“提交”按鈕時(shí),此時(shí)不再是瀏覽器的提交行為了,而是使用Ajax的方式提交,提交的URL以及提交方法就是在FORM中指定的參數(shù)。

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

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

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

      以Ajax方式提交部分表單

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

      <div id="divCustomerInfo">
      <p>客戶名稱: <input type="text" name="CustomerName" style="width: 300px" /></p>
      <p>客戶電話: <input type="text" name="CustomerTel" style="width: 300px" /></p>
      </div>
      

      我們可以這樣來提交這部分表單的數(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選擇器】來過濾要提交的控件,而不是使用成功控件的篩選邏輯。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      示例代碼分為二段,一段示范了使用application/x-www-form-urlencoded編碼方式提交,另一段則示范了使用multipart/form-data的編碼方式。
      為了讓大家能再次利用這些代碼,我已將關(guān)鍵部分寫成獨(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)記,用于寫入請(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)備寫入數(shù)據(jù)。
      Stream stream = request.GetRequestStream();
      if( keyvalues != null && keyvalues.Count > 0 ) {
      // 寫入非文件的keyvalues部分
      foreach( KeyValuePair<string, string> kvp in keyvalues ) {
      // 寫入數(shù)據(jù)塊的分隔標(biāo)記
      stream.Write(boundaryBytes, 0, boundaryBytes.Length);
      // 寫入數(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);
      }
      }
      // 寫入要上傳的文件
      foreach( KeyValuePair<string, string> kvp in fileList ) {
      // 寫入數(shù)據(jù)塊的分隔標(biāo)記
      stream.Write(boundaryBytes, 0, boundaryBytes.Length);
      // 寫入文件描述,這里設(shè)置一個(gè)通用的類型描述: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);
      // 寫入文件內(nèi)容
      byte[] body = File.ReadAllBytes(kvp.Value);
      stream.Write(body, 0, body.Length);
      }
      // 寫入結(jié)束標(biāo)記
      boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
      stream.Write(boundaryBytes, 0, boundaryBytes.Length);
      stream.Close();
      // 開始發(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", "來幾個(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);
      }
      

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

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(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)遵守用戶 評(píng)論公約

        類似文章 更多