无法获取未定义或空引用的属性"值"

0 asp.net ajax asp.net-ajax

我有注册表格和按钮.OnClick - 我在服务器端调用函数,它使用Zipcodes在Database上验证用户的邮政编码.如果验证成功通过 - 用户的数据存储在数据库中(这里我继续使用服务器功能).但是如果ZipCode不匹配 - 我调用Javascript函数,我问用户是否仍想将他的数据保存到DB.如果是 - 我使用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>
Run Code Online (Sandbox Code Playgroud)

这是我的服务器端

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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是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();
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*ien 5

关于使用"自定义"AJAX请求的客户端 - 服务器通信的简短书籍.

在ASP.net编程(几乎)每次客户端与服务器交互时,客户端将其所有信息发送到服务器,然后抛出其旧内容并将其替换为客户端从服务器收到的响应.因此,您遇到的问题是您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>
Run Code Online (Sandbox Code Playgroud)

有几件事需要注意:

  1. 提交表单的按钮不是ASP按钮,而是HTML按钮.
  2. 所有输入控件都是ASP控件,但是它们ClientIDMode设置为Static,这只适用于.NET 4.0或更高版本.
  3. 我们设置class为同样的事情ID,我们没有使用.NET 4.0或更高版本的情况.您想要添加到控件的任何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...
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,直到这整个情况的肉和土豆.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);
} 
Run Code Online (Sandbox Code Playgroud)

此函数具有内置超时,这使得如果服务器响应请求超过30秒,则忽略客户端接收的任何响应.对于我的实现,这与另一个向用户显示某些内容的功能相结合,告诉他们网站正在处理他们的请求,如果超时,则告诉他们发生了超时.

这个函数做的第二件事是它假设所有处理程序都在你的网站根目录旁边的文件夹中RequestHandlers.我使用这个设置只是为了整合我的所有处理程序文件,但你可以真正改变它在任何你想要的地方.

函数本身接受一个字符串和四个函数指针.该字符串表示将等待解释请求的处理程序的名称,这四个函数指针都具有非常特定的作业.

第一个函数指针是GetQueryString表示您必须编写的函数,它将您认为必要的任何变量附加到要发回的URL的末尾.该站点提供了对查询字符串应该用于什么的非常准确的解释.对我来说,一个常见的GetQueryString功能看起来像:

function GetPaymentQueryString() {
    var varString = '';
    varString = "?CCPayment=True";
    return varString; 
}
Run Code Online (Sandbox Code Playgroud)

第二个函数指针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;
}
Run Code Online (Sandbox Code Playgroud)

该功能的重要部分是使您的XML正确.第一个标签是非常通用的,在大多数情况下应该可以使用,然后在它之后它只是匹配标签.我遗漏了很多你的田地以节省空间.

最后两个函数指针是你想要调用的东西,如果一切按计划进行,并且分别失败了.我通常处理成功请求的方式是将输入作为一个整体隐藏(通常将它们放在自己的div部分中)并显示某种确认消息.失败的请求可能有点棘手,因为您必须告诉用户失败的原因.我这样做的方法是div在页面上的其他所有内容上面放置一个虚拟部分,并附加某种特殊的CSS div,以某种方式突出显示,如果请求失败,那么我从服务器发送一串文本我最好猜测它失败的原因并将其分配给该div部分中显示的内容.您决定如何向用户显示结果显然都是由项目本身决定的.因为你成功或失败时所做的事情基本上是逐个项目的,所以我不能真正给出你应该做的一个很好的通用例子,因为这部分你是靠自己的.

现在我们已经有了这些部分,最后要做的就是处理程序.

基本上对于所有意图和目的,处理程序基本上是一个ASPX没有任何内容的网页.因此HTML,构成具有扩展名的处理程序页面.ashx将如下所示:

<%@ WebHandler Language="VB" CodeBehind="YourHandler.ashx.cs" Class="YourHandler" %>
Run Code Online (Sandbox Code Playgroud)

就是这样.实际.ashx文件中不应该有其他标记.显然,处理程序的名称将根据您的操作而改变.

ashx默认情况下创建文件时的代码将是一个包含名为的单个函数的类ProcessRequest.基本上,您可以将此功能视为一种"请求已接收"事件.因此,在您的情况下,您将btnSubmit_Click函数的内容移动到文件中的ProcessRequest函数ashx.您可以添加所需的任何属性或其他功能,但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;
Run Code Online (Sandbox Code Playgroud)

为了将XML发送回客户端,您需要在响应中添加几个标头,然后通过添加来实现这一点(我认为这是在C#中实现接口的正确方法,但在这一点上不是正面的):

class YourHandler : System.Web.IHttpHandler, System.Web.SessionState.IReadOnlySessionState
Run Code Online (Sandbox Code Playgroud)

在你的班级定义下:

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

到你的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();
}
Run Code Online (Sandbox Code Playgroud)

在客户端,FriendlyMessage您需要从XML 获取:

xml.getElementsByTagName("FriendlyMessage")[0].childNodes[0].nodeValue
Run Code Online (Sandbox Code Playgroud)

现在这一行做了一些假设.比如,您可能想要添加一些检查以确保xml.getElementsByTagName("FriendlyMessage")在尝试评估它们之前确实有孩子.这些检查由您自行决定.

这次我想我实际上已经涵盖了所有步骤.我希望我的"小"指南可以帮助你,而且我没有太多烦你.我为长度道歉,但它是一个过程,所以正确的步骤需要几步.一旦你获得了基线和工作,它真的适合任何情况.这种布局还使您的用户体验比让他们每次等待完全访问服务器更好.

我真诚地希望这可以帮助你完成你的项目,并且我没有跳过一个同样令人尴尬的步骤......