2013-05-15 70 views
0

我有註冊表單和按鈕。 OnClick - 我在服務器端調用函數,用Zipcodes在數據庫中驗證用戶的郵政編碼。如果驗證通過成功 - 用戶數據存儲在數據庫中(這裏我繼續使用服務器功能)。但是,如果ZipCode不匹配 - 我調用Javascript函數,如果用戶仍然想要將他的數據保存到數據庫。如果是 - 我使用Ajax請求保存它。問題是當我調用Javascript函數時 - 首先它應該在客戶端接收用戶數據。但是,當讀數據發生時 - 我收到一個錯誤「無法獲取未定義或空引用的屬性值」。但用戶的數據仍然存在於表單的字段中。看起來服務器從表單讀取的數據 - 在某處重置 - 並且不能在客戶機上第二次讀取。 這裏是我的ASP形式無法獲取未定義或空引用的屬性'值'

<body> 
    <form id="frmZipValidation" runat="server"> 
    <div> 
     <asp:Label runat="server">Registration Form</asp:Label> 
     <asp:TextBox runat="server" ID="txtbxName"></asp:TextBox> 
     <asp:TextBox runat="server" ID="txtbxZipCode"></asp:TextBox>    
     <asp:DropDownList runat="server" ID="DDLCountry"> 
      <asp:ListItem Text="Select country" Value="Select" Selected="True"></asp:ListItem> 
      <asp:ListItem Text="USA" Value="USA"></asp:ListItem> 
      <asp:ListItem Text="Canada" Value="Canada"></asp:ListItem> 
     </asp:DropDownList> 
     <asp:TextBox runat="server" ID="txtbxState"></asp:TextBox> 
     <asp:TextBox runat="server" ID="txtbxCity"></asp:TextBox> 
     <asp:Button runat="server" ID="btnSubmit" Text="Submit" OnClick="btnSubmit_Click"/> 
    </div> 
    </form> 
</body> 

這裏是我的服務器端

public partial class Default : System.Web.UI.Page 
{ 
    string Name; 
    string ZipCode; 
    string Country; 
    string State; 
    string City; 
    bool IsMatch; 
    Addresses dbAddresses = new Addresses(); 
    User newUser; 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     if (Request["Action"] != null && Request["Action"].Trim() != "") 
     { 
      if (Request["Action"] == "AddUser") 
      { 
       AddUser(Request["Name"], Request["ZipCode"], Request["Country"], Request["State"], Request["City"]); 
      } 
     } 
    } 

    private void AddUser(string UserName, string UserZip, string UserCountry, string UserState, string UserCity) 
    { 
     newUser = new User(UserName, UserZip, UserCountry, UserState, UserCity); 
     dbAddresses.Users.Add(newUser); 
     dbAddresses.SaveChanges(); 
    } 

    protected void btnSubmit_Click(object sender, EventArgs e) 
    { 
     if (IsValid) 
     { 
      ZipCode = txtbxZipCode.Text; 
      Country = DDLCountry.Text; 
      State = txtbxState.Text; 
      City = txtbxCity.Text; 
      Name = txtbxName.Text; 
      IsMatch = false; 

      List<ZipCode> ZipC = (from z in dbAddresses.Zips 
            where z.Zip == ZipCode 
            select z).ToList(); 

      //If ZipCode entered by client do not exists at Database return false 
      if (!ZipC.Any()) 
      { 
       IsMatch = false; 
      } 
      else 
      {      
       for (int i = 0; i < ZipC.Count; i++) 
       { 
        if (ZipC[i].Country.ToString() == Country) 
        { 
         if (ZipC[i].State.ToString() == State) 
         { 
          if (ZipC[i].City.ToString() == City) 
          { 
           AddUser(Name, ZipCode, Country, State, City); 

           //Message to the user that all saved successfully 
           Page.ClientScript.RegisterClientScriptBlock(typeof(Page), "1", "<script>alert('Your data was saved successfully!');</script>"); 
           IsMatch = true; 
           break; 
          } 
          else 
          { 
           IsMatch = false; 
           break; 
          } 
         } 
         else 
         { 
          IsMatch = false; 
          break; 
         } 
        } 
        else 
        { 
         IsMatch = false; 
         break; 
        } 
       } 
      } 
      //If user's data are not match, then go to JS client code where - If user wants in any case to save data - make it using AJAX request 
      if (!IsMatch) 
      { 
       string clientScript = "AjaxRequestSaveToDB();"; 
       this.Page.ClientScript.RegisterStartupScript(this.GetType(), "MyClientScript", clientScript); 
      } 
     } 
    } 
} 

這裏是使用Javascript:

function AjaxRequestSaveToDB() 
{ 
var SaveData = confirm('Zip/Postal code doesn’t match region. Are you sure you want to save this data?'); 

if (SaveData) 
    { 
    var UserName = document.getElementById('txtbxName').value; 
    var UserZipCode = document.getElementById('txtbxZipCode').value; 
    var UserCountry = document.getElementById('DDLCountry').value; 
    var USerState = document.getElementById('txtbxState').value; 
    var UserCity = document.getElementById('txtbxCity').value; 
    SendDataToServer('AddUser', UserName, UserZipCode, UserCountry, USerState, UserCity); 
    alert("You data was saved successfully!"); 
    } 
else { alert('Not saved'); 
    } 
    } 
} 

function SendDataToServer(RequestType, Name, ZipCode, Country, State, City) 
{ 
    var xmlHttp = getXmlHttp(); 

    var Url = "Default.aspx?Action=" + escape(RequestType) 
    + "&Name=" + escape(Name) 
    + "&ZipCode=" + escape(ZipCode) 
    + "&Country=" + escape(Country) 
    + "&State=" + escape(State) 
    + "&City=" + escape(City); 

    xmlHttp.open("GET", Url, true); 
    xmlHttp.send(); 
} 
+1

如果不是看到的是,郵政編碼是空的服務器,你用什麼一個html按鈕,並在按鈕被點擊後立即在客戶端執行檢查,然後在需要時從那裏觸發回發?我認爲這個問題是,ASP按鈕正在完成一個完整的頁面回發和刷新,所以當您在回發後調用javascript函數時,用戶輸入的所有值都遠遠超出頁面的範圍。 –

+0

我嘗試從一開始就獲取客戶端的所有數據,然後將它們傳輸到服務器以驗證數據庫,但是當我需要切換回客戶端時 - 程序不會進行轉換。它只是不能識別JavaScript函數的調用代碼。我寫它爲:Page.ClientScript.RegisterClientScriptBlock(typeof(Page),「1」,「」);我甚至試着用簡單的「警報」 - 不起作用... – Pancheska

+0

不幸的是,我認爲你最好的選擇是讓網頁做你想做的事情,而且爲了最簡化的用戶體驗將幾乎是一個完整的重寫,使用客戶端控件(可能帶有jQuery驗證),帶有XML內容的AJAX請求和一個asp.net處理程序頁面。當我獲得免費分鐘時,我會爲你寫出完整的答案。與此同時[本網站](http://www.w3schools.com/ajax/ajax_xmlhttprequest_create.asp)相當徹底地超越了自定義ajax請求。 –

回答

5

有關客戶端服務器通信短書使用 「自定義」 AJAX要求。每當客戶端與服務器交互時,客戶端都會將其所有信息發送到服務器,然後拋出其舊內容,並用客戶端從服務器接收到的響應替換它。所以,你正在運行到的問題是,在客戶端機器上的asp:button是您.aspx頁面發送信息的服務器和服務器正在演繹的信息,實現東西是錯誤的,並告訴客戶應該要求用戶提供更多的信息,但丟棄之前輸入的所有信息。

,我已經找到了解決這個問題的最好辦法就是用我稱之爲「定製AJAX請求。」基本上,這意味着我們編寫一個XML字符串並將其發送到ASP處理程序頁面,該頁面設置爲接受XML字符串並對其執行操作。在我的旅行中,我已經將其減少到基本上3個部分。第一個是包含所有標記和CSS(以及驗證)的用戶界面,第二個是包含所有數據收集和實際AJAX請求的JavaScript文件,最後是處理來自第三方應用程序的請求的ashx文件。客戶。

所以開始你將需要設置你的用戶界面。沿着線的東西:

<body> 
    <form id="frmZipValidation" runat="server"> 
    <div> 
     <div class="label">Registration Form<div> 
     <asp:TextBox ID="txtbxName" class="txtbxName" ClientIDMode="Static" runat="server"></asp:TextBox> 
     <asp:TextBox ID="txtbxZipCode" class="txtbxZipCode" ClientIDMode="Static" runat="server" ></asp:TextBox>    
     <asp:DropDownList ID="DDLCountry" class="DDLCountry" ClientIDMode="Static" runat="server" > 
      <asp:ListItem Text="Select country" Value="Select" Selected="True"></asp:ListItem> 
      <asp:ListItem Text="USA" Value="USA"></asp:ListItem> 
      <asp:ListItem Text="Canada" Value="Canada"></asp:ListItem> 
     </asp:DropDownList> 
     <asp:TextBox ID="txtbxState" class="txtbxState" ClientIDMode="Static" runat="server" ></asp:TextBox> 
     <asp:TextBox ID="txtbxCity" class="txtbxCity" ClientIDMode="Static" runat="server" ></asp:TextBox> 
     <input id="btnSubmit" class="btnSubmit" type="button" value="Save" onclick="SubmitForm()" /> 
    </div> 
    </form> 
</body> 

幾件事情要注意這一點:

  1. 按鈕提交表單不是一個ASP按鈕,但一個HTML按鈕。
  2. 所有輸入控件是ASP的控制,但他們有ClientIDMode設置爲Static,這隻會工作,在.NET 4.0或更高版本。
  3. 我們在情況下,ID我們沒有使用.NET 4.0或更高版本設置class以同樣的事情。任何你想添加到控件中的CSS類都可以添加到虛擬ID類之後(對於我的例子,我假設你在.NET 4.0中,但是如果你需要,我可以很容易地將它們切換到沒有ClientIDMode屬性的工作狀態)

第二一塊拼圖是JavaScript。有幾種方法可以實現我們所需的功能。第一種是在沒有任何插件或外部庫的幫助下使用vanilla JS。這節省了非常少量的處理時間,少量的加載時間,並且可以完成我們所要求的一切。但是,如果我們包含外部庫,JQuery和插件JQuery Validation,那麼在編程階段,我們可以通過將我們必須編寫的代碼量減少約10倍。如果我們真的關心加載時間,那麼我們可以使用客戶端緩存來存儲外部庫,以便他們只需要下載一次。因此,無論您是否決定使用任何外部JavaScript庫,都取決於您的項目需求,但由於您只關心驗證郵政編碼是否爲空,因此我不會使用任何JQuery,但我只是認爲它值得一提,因爲它是如何簡化流程的。

準備好提交表單後,您的第一步就是驗證郵編是否有效。取決於你想要獲得的深度,你可以通過幾種方法來做到這一點。最快的檢查只是驗證按鈕被點擊時郵政編碼文本框不是空的。因此,要做到這一點,我們就只需要做:

function SubmitForm() { //This will be assigned as the click handler on your button in your HTML 
    if (document.getElementById('txtbxZipCode').value != null && document.getElementById('txtbxZipCode').value != '') { 
     Save('YourHandler', GetQueryString, GetXmlString, SuccessHandler, FailureHandler); 
    } else { 
     //Your user needs to know what went wrong... 
    } 
} 

所以,下降到這個整體形勢的香餑餑。 AJAX請求。我想出了一個處理整個AJAX請求,看起來像一個可重複使用的功能:

function Save(handlerName, GetQueryString, GetXmlString, SuccessHandler, FailureHandler) { 
    // Date.GetTime gets the number of milliseconds since 1 January 1970, so we divide by 1000 to get the seconds. 
    end = (new Date().getTime()/1000) + 30; 
    //This variable is the actual AJAX request. This object works for IE8+ but if you want backwards compatability for earlier versions you will need a different object which I can dig up for you if you need. 
    var xmlhttp = new XMLHttpRequest(); 
    //This is the function that fires everytime the status of the request changes. 
    xmlhttp.onreadystatechange = function() { 
     if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 
      //Get all the headers to determine whether or not the request was successful. This is a header you will need to add to the response manually. 
      var xx = xmlhttp.getResponseHeader("Success"); 
      //the object xx will be a string that you designate. I chose to use True as the indicator that it was successful because it was intuitive. 
      var x1 = xx.trim(); 
      if (x1 != undefined && x1 == 'True' && (new Date().getTime()/1000) < end) { 
       //If the response was successful and the timeout hasn't elapsed then we get the XML from the response and call the success handler 
       var xmlResponse = xmlhttp.responseXML; 
       SuccessHandler(sender, xmlResponse); 
      } else if ((new Date().getTime()/1000) < end) { 
       //If the response was not successful and the timeout hasn't elapsed then we get the XML from the response and call the failure handler 
       var xmlResponse = xmlhttp.responseXML; 
       FailureHandler(sender, xmlResponse); 
      } //If the request was successful 
     } //If the readystate is 4 and the status is 200 
    } //OnReadyStateChanged function 

    //This gets the query string to be added to the url 
    var varString = GetQueryString(); 

    //Build XML string to send to the server 
    var xmlString = GetXmlString(); 

    //Open the request using the handler name passed in and the querystring we got from the function passed in 
    xmlhttp.open("POST", "../RequestHandlers/" + handlerName + ".ashx" + varString, true); 
    //This tells the handler that the content of the request is XML 
    xmlhttp.setRequestHeader("Content-Type", "text/xml"); 
    //Send the request using the XML we got from the other function passed in. 
    xmlhttp.send(xmlString); 
} 

這個功能有一個內置的超時,這使得它,這樣如果服務器需要超過30秒的響應請求,那麼客戶收到的任何響應都會被忽略。對於我的實現,這與另一個功能相結合,該功能向用戶顯示一些信息,告訴他們該網站正在處理他們的請求,如果超時已過,則會告訴他們發生了超時。

該函數的第二件事是假定所有的處理程序都將位於名爲RequestHandlers的網站根目錄旁邊的文件夾中。我使用這個設置來合併所有的處理程序文件,但是您可以真正地改變它在哪裏尋找到的位置。

該函數本身需要一個字符串和四個函數指針。該字符串表示將等待解釋請求的處理程序的名稱,四個函數指針都具有非常特定的作業。

第一個函數指針是GetQueryString這代表一個函數,你將不得不編寫這個函數,它會將你認爲需要的變量附加到所返回的URL的末尾。 This site給出了查詢字符串應該用於什麼的相當準確的解釋。對我來說,一個共同的GetQueryString函數看起來是這樣的:

function GetPaymentQueryString() { 
    var varString = ''; 
    varString = "?CCPayment=True"; 
    return varString; 
} 

第二個函數指針,GetXMLString,用於創建XML字符串(去圖),他們會被髮送到我們張貼處理頁面回到。該字符串將代表請求的大部分。所有不應該顯示給任何窺探你的請求的人應該以XML字符串的形式發送,如果你真的偏執,你可以把它作爲一個加密的XML字符串發送,但是嚴格來說這不是必須的。這一切都取決於你發送的內容,如果它的完整的信用卡信息,那麼,也許你會想要考慮它,但如果它的名字和姓氏然後加密它將是矯枉過正。

一個共同GetXMLString功能可能看起來像:

function GetPaymentXmlString() { 
    var xmlString = ''; 
    xmlString = '<?xml version="1.0" encoding="UTF-8"?><Address><ZipCode>' + document.getElementById('txtbxZipCode').value + '</ZipCode></Address>'; 
    return xmlString; 
} 

該功能的重要組成部分,是讓你的XML正確。第一個標籤非常普遍,應該在大多數情況下都可以使用,然後在所有標籤上匹配標籤。爲了節省空間,我遺漏了很多你的領域。

最後兩個函數指針是你想調用的,如果一切按計劃進行並且分別失敗了。我通常處理成功請求的方式是將輸入作爲一個整體隱藏起來(通常通過將它們放在自己的div部分中)並顯示某種確認消息。失敗的請求可能有點棘手,因爲你必須告訴用戶他們爲什麼失敗。我這樣做的方式是通過在頁面上放置一個虛擬的div部分,並附上某種特殊的CSS,使得div以某種方式脫穎而出,如果請求失敗,我會發送一串文本服務器以我最好的猜測爲什麼失敗並將其分配給顯示在div部分。你如何決定將結果顯示給用戶顯然都是由項目本身決定的。既然成功或失敗時所做的工作基本上是按項目進行的,我無法給出一個很好的通用示例,說明你應該如何做,因爲這部分是你自己做的。

現在我們已經有了這些作品,最後做的作品就是處理程序。

基本上對於所有意圖和目的,處理程序基本上是一個沒有任何內容的ASPX網頁。所以HTML,使您的處理程序的網頁,其中有擴展.ashx,看起來像:

<%@ WebHandler Language="VB" CodeBehind="YourHandler.ashx.cs" Class="YourHandler" %> 

就是這樣。實際.ashx文件中應該沒有其他標記。很明顯,處理程序的名稱將根據你在做什麼而改變。

默認情況下創建ashx文件時,後面的代碼將是一個類,其中包含一個名爲ProcessRequest的函數。基本上你可以把這個功能當作一種「請求接收」事件。因此,在您的情況下,您可以將btnSubmit_Click函數的內容移動到ashx文件中的ProcessRequest函數。您可以添加所需的任何屬性或其他功能,但ProcessRequest函數必須存在才能使處理程序正常工作,據我所知。

您需要做的一個額外步驟是從發送到您的處理程序的XML中獲取信息,並告訴響應您將XML發送回客戶端。

因此,要獲得來自請求的XML,你需要做的事:

IO.StreamReader textReader = New IO.StreamReader(context.Request.InputStream); 
context.Request.InputStream.Seek(0, IO.SeekOrigin.Begin); 
textReader.DiscardBufferedData(); 
XDocument xml = XDocument.Load(textReader); 
String zip = xml.Elements("Address").Elements("ZipCode").FirstOrDefault().Value; 

爲了發送XML回客戶端,你需要一對夫婦的頭添加到響應,你完成,通過加入(我認爲這是儘管C#實現的接口在這一點上並不積極正確的方式):

class YourHandler : System.Web.IHttpHandler, System.Web.SessionState.IReadOnlySessionState 

類定義下和:

context.Response.ContentType = "text/xml"; 
context.Response.ContentEncoding = System.Text.Encoding.UTF8; 
context.Response.Cache.SetCacheability(HttpCacheability.NoCache); 
context.Response.Cache.SetAllowResponseInBrowserHistory(True); 

ProcessRequest函數的開頭。這六行告訴客戶端它將接收XML,而不是緩存任何響應,這將確保您的客戶始終能夠看到最新的內容。

所以。它是。您現在應該有框架來驗證用戶輸入,創建AJAX請求,將請求發送到自定義處理程序,接受來自客戶端的XML,將XML寫入客戶端並顯示res -...我知道我忘記了一些東西。

客戶端應該如何處理從服務器獲得的XML?把它扔在牆上,看看有什麼棒?不,那不行。您需要一種方法來解釋客戶端的XML。幸運的是,編寫了XMLHttpRequest對象使得這個任務比聽起來容易得多。

您可能已經注意到,我設置了成功和失敗處理程序來接收發件人對象和XML對象。發件人確實是矯枉過正,可以忽略(或刪除)這個例子的正常工作。 XML對象就是我們現在所關心的。在進入客戶端之前,我必須提到您必須在服務器端執行與客戶端相同的過程,並手動編寫您的XML字符串,包括您希望客戶端了解的所有值。對於這個例子,我假設你想要給用戶顯示一個FriendlyMessage。要寫入到客戶端,你會做一些這樣的迴應:

using (System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(context.Response.Output)) { 
    context.Response.AddHeader("Success", true); 
    System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); 
    doc.LoadXml("<?xml version='1.0' encoding='UTF-8'?><Response><FriendlyMessage>" + Message + "</FriendlyMessage></Response>"); 
    doc.WriteTo(writer); 
    writer.Flush(); 
    writer.Close(); 
} 

在客戶端從XML獲得FriendlyMessage你需要做的:

xml.getElementsByTagName("FriendlyMessage")[0].childNodes[0].nodeValue 

現在這條線做了幾假設。就像,你可能想要添加一些檢查,以確保xml.getElementsByTagName("FriendlyMessage")實際上有孩子之前試圖評估他們。這些檢查由您自行決定。

這次我想我已經涵蓋了所有的步驟。我希望我的「小小」指南能夠幫助你,而且我也不會讓你太擔心。我對這個長度表示歉意,但是它的一個過程是這樣的,所以只需要幾個步驟。一旦你建立了基線並開展工作,它確實適用於任何情況。這種佈局還使您的用戶體驗比每次等待服務器全程旅行都要好得多。

我衷心希望這可以幫助你得到你的項目做,我沒有跳過的步驟或者其他同等尷尬......

+0

非常感謝您的幫助!我需要學習它。據我瞭解你使用VB?正如我使用C#。所以我需要先將服務器代碼轉換爲C#。 – Pancheska

+0

ahh我更喜歡VB.NET。我已經轉換了代碼,但由於我不熟悉C#,我可能犯了一個錯誤。 –

相關問題