0

登録フォームとボタンがあります。OnClick - サーバー側で関数を呼び出して、Zipcodes を使用してデータベースでユーザーの郵便番号を検証します。検証に合格した場合 - ユーザーのデータはデータベースに保存されます (ここでは引き続きサーバー機能を使用します)。しかし、ZipCode が一致しない場合は、Javascript 関数を呼び出して、ユーザーがまだ自分のデータを DB に保存するかどうかを尋ねます。はいの場合-Ajaxリクエストを使用して保存します。問題は、Javascript 関数を呼び出すときです。まず、クライアント側でユーザーのデータを受け取る必要があります。しかし、データの読み取りが発生すると、「未定義または null 参照のプロパティ '値' を取得できません」というエラーが表示されます。しかし、ユーザーのデータはフォームのフィールドにまだ存在しています。サーバーがフォームから一度読み取ったデータは、どこかでリセットされ、クライアントで再度読み取ることができないようです。

<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();
}
4

1 に答える 1

5

「カスタム」AJAX リクエストを使用したクライアント サーバー通信に関する短い本。

ASP.net プログラミングでは、(ほぼ) クライアントがサーバーと対話するたびに、クライアントはすべての情報をサーバーに送信し、古いコンテンツを破棄して、クライアントがサーバーから受信した応答に置き換えます。したがって、あなたが直面していた問題はasp:button、クライアントマシン上のあなた.aspxがサーバー上のあなたのページに情報を送信していて、サーバーが情報を解釈していて、何かが間違っていることに気づき、クライアントにユーザーに詳細を尋ねる必要があることを伝えていましたが、捨てました.以前に入力されたすべての情報。

この問題を回避するために私が見つけた最善の方法は、私が「カスタム AJAX 要求」と呼んでいるものを使用することです。基本的にこれは、XML の文字列を作成し、それを ASP ハンドラ ページに送信することを意味します。ASP ハンドラ ページは、XML 文字列を受け入れて何かを行うように設定されています。私の旅行では、これを基本的に 3 つの部分に減らしました。1 つ目は、すべてのマークアップと CSS (および検証) を含むユーザー インターフェイスです。2 つ目は、すべてのデータ収集と実際の 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. .NET 4.0 以降を使用していない場合はclass、 を と同じものに設定します。IDコントロールにも追加したい CSS クラスは、ダミー ID クラスの後に追加できますClientIDMode

パズルの 2 番目のピースは JavaScript です。必要なことを達成するには、いくつかの方法があります。1 つ目は、プラグインや外部ライブラリの助けを借りずにバニラ JS を使用することです。これにより、処理時間がわずかに短縮され、ロード時間がわずかに短縮され、求められるすべてのことを達成できます。しかし、外部ライブラリである JQuery とプラグインである JQuery Validation を含めると、プログラミング段階で、記述しなければならないコードの量が約 10 分の 1 に減り、作業が大幅に簡素化されます。ロード時間が本当に気になる場合は、クライアント キャッシュを使用して外部ライブラリを保存し、一度ダウンロードするだけで済むようにします。

フォームを送信する準備ができたら、最初のステップは、郵便番号が有効であることを検証することです。取得する深さに応じて、いくつかの方法でこれを行うことができます。最も簡単なチェックは、ボタンをクリックしたときに郵便番号のテキスト ボックスが空でないことを確認することです。そのためには、次のことを行う必要があります。

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 秒以上かかる場合、クライアントが受け取る応答はすべて無視されます。私の実装では、これを別の関数と組み合わせて、ユーザーに何かを表示して、Web サイトが要求に取り組んでいることを伝え、タイムアウトが経過すると、タイムアウトが発生したことを伝えます。

この関数が行う 2 番目のことは、すべてのハンドラーが という名前の Web サイトのルートの隣にあるフォルダーにあると想定することRequestHandlersです。このセットアップは、すべてのハンドラー ファイルを統合するためだけに使用しますが、必要な場所に変更することができます。

関数自体は、文字列と 4 つの関数ポインターを受け取ります。文字列は、リクエストの解釈を待機するハンドラーの名前を表します。4 つの関数ポインターはすべて、非常に特殊なジョブを持っています。

最初の関数ポインタはGetQueryString、ポストバック先の URL の末尾に必要と思われる変数を追加する、作成する必要がある関数を表します。このサイトでは、クエリ文字列の使用目的についてかなり正確に説明しています。私にとって、一般的なGetQueryString関数は次のようになります。

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

2 番目の関数ポインタ は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 を正しく取得することです。最初のタグは非常に普遍的で、ほとんどの状況で問題なく使用でき、その後はタグを一致させるだけです。スペースを節約するために、多くのフィールドを省略しました。

最後の 2 つの関数ポインターは、すべてが計画どおりに進んだ場合と何かが失敗した場合にそれぞれ呼び出す必要があります。私が通常、成功したリクエストを処理する方法は、入力を全体として非表示にし (通常は、入力を独自のdivセクション内に配置することによって)、何らかの確認メッセージを表示することです。失敗したリクエストは、失敗した理由をユーザーに伝える必要があるため、少し複雑になる可能性があります。div私がそれを行う方法は、ページ上の他のすべての上にダミーセクションを作成divし、何らかの方法で目立たせる特別な CSS を添付することです。リクエストが失敗した場合は、サーバーからテキストの文字列を送信します。失敗した理由を推測し、divセクション。結果をユーザーに表示する方法は、明らかにすべてプロジェクト自体によって決定されます。成功または失敗したときに行うことは、基本的にプロジェクトごとに異なるため、この部分について自分で行うべきことの一般的な例を示すことはできません。

これらのパーツが配置されたので、最後に作成するのはハンドラーです。

基本的に、すべての意図と目的において、ハンドラーは基本的ASPXに何もない Web ページです。したがって、HTML拡張子 を持つハンドラ ページを構成する は次の.ashxようになります。

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

以上です。.ashx実際のファイルには他のマークアップがあってはなりません。明らかに、ハンドラーの名前は、何をしているかによって変わります。

デフォルトでファイルを作成するときのコード ビハインドashxは、 という名前の単一の関数を含むクラスになりますProcessRequest。基本的に、この関数は一種の「リクエスト受信」イベントとして扱うことができます。したがって、あなたの場合、関数の内容をbtnSubmit_Clickファイル内のProcessRequest関数に移動しashxます。必要なプロパティやその他の関数を追加できますが、ProcessRequest私が知る限り、ハンドラーが機能するには関数が存在する必要があります。

実行する必要がある 1 つの追加手順は、ハンドラーに送信された 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関数の先頭に。これらの 6 行は、クライアントが XML を受信し、応答をキャッシュしないことをクライアントに伝えます。これにより、クライアントは常に最新のコンテンツを見ることができます。

そう。そこにそれがある。これで、ユーザー入力の検証、AJAX 要求の作成、カスタム ハンドラーへの要求の送信、クライアントからの XML の受け入れ、クライアントへの XML の書き込み、および応答の表示を行うためのフレームワークができました。 .

クライアントは、サーバーから取得した XML をどのように扱うべきでしょうか? 壁に投げて、何がくっつくか見てみませんか?いいえ、うまくいきません。クライアント側で XML を解釈する方法が必要になります。幸いなことに、このXMLHttpRequestオブジェクトは、このタスクを思ったよりもずっと簡単に行えるように作成されています。

お気付きかもしれませんが、sender オブジェクトと XML オブジェクトを受け取るように成功ハンドラーと失敗ハンドラーをセットアップしました。送信者は本当にやり過ぎであり、この例が正常に機能するために無視 (または削除) することができます。XML オブジェクトは、今私たちが関心を持っているものです。クライアント側に入る前に、クライアント側で行ったのと同じプロセスをサーバー側で実行し、クライアントに知ってもらいたいすべての値を含む XML 文字列を手動で記述する必要があることに言及する必要があります。この例ではFriendlyMessage、ユーザーに a を表示すると仮定します。クライアントへの応答を書き込むには、次のようにします。

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();
}

クライアント側FriendlyMessageで XML から を取得するには、次のことを行う必要があります。

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

ここで、この行はいくつかの仮定を行います。同様に、いくつかのチェックを追加してxml.getElementsByTagName("FriendlyMessage")、評価を試みる前に実際に子があることを確認することもできます。この種のチェックは、あなたの裁量に任されています。

今回は、実際にすべての手順をカバーしたと思います。私の「小さな」ガイドがお役に立てば幸いです。あまり退屈しませんでした。長々と申し訳ありませんが、そのようなプロセスであるため、適切に処理するにはいくつかの手順が必要です。ベースラインを設定して機能させれば、どんな状況にも対応できます。このレイアウトは、毎回サーバーへの完全なトリップを待つよりも、ユーザー エクスペリエンスを大幅に向上させます。

これがあなたのプロジェクトの完了に役立つことを心から願っています。恥ずかしいほどステップや何かをスキップしていないことを願っています...

于 2013-05-16T02:35:22.763 に答える