ASP.NET "Support Voice"
Dynamische Seitenaktualisierungen mithilfe von XMLHTTP
Um diese Spalte an Ihre Anforderungen anzupassen, möchten wir Sie einladen, Ihre Ideen zu Themen zu übermitteln, die Sie interessieren, sowie zu Problemen, die Sie in zukünftigen Knowledge Base-Artikeln und Supportstimmen behandelt sehen möchten. Sie können Ihre Ideen und Ihr Feedback mithilfe des Formulars "Fragen" übermitteln. Unten in dieser Spalte gibt es auch einen Link zum Formular.
EINFÜHRUNG
Eine meiner bevorzugten Methoden zum Untersuchen der Nutzbarkeit von Webanwendungen ist es, meine Frau beim Navigieren auf einer Website zu beobachten. Sie kann sich im Internet recht gut durcharbeiten, kennt aber nur wenige der technischen Aspekte auf unterer Ebene (was sie als "langweilige Dinge" bezeichnet), die dafür sorgen, dass alles funktioniert.
An einem kürzlichen Abend schaute ich mir an, wie meine Frau eine E-Commerce-Anwendung eines der großen Jungen verwendet. Sie hat mithilfe mehrerer Dropdownlisten ein Drill down in eine Produktauflistung geschaltet, von der jede zuvor getroffene Auswahl auftüttert. Wenn sie in jeder Dropdownliste auf ein Element geklickt hat, wurde die Seite wieder gepostet, um Daten für die nächste Dropdownliste zu erhalten. Die Erfahrung war für sie frustrierend, da ihr Eindruck war, dass es aufgrund der Rücksende des Beitrag sehr lange ge dauern hat. Der Frust, den sie hatte, könnte von den Entwicklern der Anwendung leicht entschärfungiert worden sein, wenn sie xmlHTTP nur zum Abrufen der Daten verwendeten, statt sie zurück posten. Darum geht es in der Spalte dieses Monats. Ich zeige Ihnen, wie Sie mithilfe von XMLHTTP einen Teil einer Webseite mit Daten aus einem Microsoft ASP.NET-Webdienst aktualisieren, ohne einen Beitrag zurück posten zu müssen. Das wird wirklich cool! Vertraue mir.Allgemeine Übersicht
XMLHTTP sendet eine Anforderung vom Client an den Webserver und gibt eine XML-Dateninsel zurück. Abhängig von der Struktur der empfangenen XML können Sie XSLT oder das XML-DOM verwenden, um sie zu bearbeiten und Teile der Seite an diese Daten zu binden. Dies ist eine äußerst leistungsstarke Technik.
NoteMicrosoft bietet ein Webdienstverhalten für Internet Explorer, das asynchrone Aufrufe von ASP.NET Webdiensten schnell und einfach macht. Dieses Verhalten wird jedoch nicht unterstützt und ist nicht die beste Methode, um eine Seite asynchron zu aktualisieren. Verwenden Sie stattdessen XMLHTTP! In dem Beispiel, das ich in dieser Spalte durcharbeite, ernenne ich drei Webdienstaufrufe an einen ASP.NET Webdienst über XMLHTTP. Der Webdienst abfraget die "Northwind"-Datenbank im lokalen SQL Server und gibt ein DataSet in Form eines XML-Diffgrams an den Client zurück. Anschließend verwende ich das XML-DOM, um diese XML-Daten zu analysieren und Teile meiner Seite dynamisch zu aktualisieren. All dies erfolgt ohne vorherigen Beitrag.Der Webdienst
Der Webdienst, den ich verwende, heißt "DynaProducts". Es handelt sich um ASP.NET webdienst, der in C# geschrieben ist und die folgenden drei Methoden enthält.
-
GetCategories – Gibt ein DataSet zurück, das alle Kategorien in der Tabelle "Categories" enthält.
-
GetProducts – Gibt ein DataSet zurück, das alle Produkte der Kategorie enthält, die an die Methode übergeben werden.
-
GetProductDetails – Gibt ein DataSet zurück, das Details zu dem Produkt enthält, dessen ProductID an die Methode übergeben wird.
Die Seite "HTML"
Das erste, was Ihnen bei diesem Beispiel auf den Ersten streicht, ist, dass die Seite, die ich aktualisiert, obwohl der ASP.NET Webdienst keine ASP.NET ist. Es ist nur eine normale HTML-Seite. Ich habe der Seite jedoch relativ viel clientseitiges JavaScript hinzugefügt, und das Skript ruft den Webdienst auf.
Werfen wir einen Blick auf den ersten Codeausschnitt der HTML-Seite.var objHttp;
var objXmlDoc; function getDataFromWS(methodName, dataSetName, wsParamValue, wsParamName) { // create the XML object objXmlDoc = new ActiveXObject("Msxml2.DOMDocument"); if (objXmlDoc == null) { alert("Unable to create DOM document!"); } else { // create an XmlHttp instance objHttp = new ActiveXObject("Microsoft.XMLHTTP"); // Create the SOAP Envelope strEnvelope = "<soap:Envelope xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + " xsd=\"http://www.w3.org/2001/XMLSchema\"" + " soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" + " <soap:Body>" + " <" + methodName + " xmlns=\"http://jimcoaddins.com/DynaProducts\">" + " </" + methodName + ">" + " </soap:Body>" + "</soap:Envelope>"; // Set up the post objHttp.onreadystatechange = function(){ // a readyState of 4 means we're ready to use the data returned by XMLHTTP if (objHttp.readyState == 4) { // get the return envelope var szResponse = objHttp.responseText; // load the return into an XML data island objXmlDoc.loadXML(szResponse); if (objXmlDoc.parseError.errorCode != 0) { var xmlErr = objXmlDoc.parseError; alert("You have error " + xmlErr.reason); } else { switch(dataSetName) { case "CategoriesDS": processCategory(); break; case "ProductsDS": processProducts(); break; case "ProductDetailDS": processProductDetails(); break; } } } } var szUrl; szUrl = "http://dadatop/wsXmlHttp/DynaProducts.asmx/" + methodName; if (wsParamValue != null) { szUrl += "?" + wsParamName + "=" + wsParamValue; } // send the POST to the Web service objHttp.open("POST", szUrl, true); objHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); objHttp.send(strEnvelope); } }
Dies ist der größte Codeteil der Seite, und ich möchte ihn im Detail erläutern, damit Sie verstehen, was vor sich geht.
Am oberen Rand dieses Skriptblocks habe ich zwei Variablen erstellt: objHttp und objXmlDoc. Dies sind die Variablen, die ich für mein XMLHTTP-Objekt und mein XML-DOM-Objekt verwenden werde. Unmittelbar danach befindet sich die Funktionsdefinition für die getDataFromWS-Funktion. Dies ist die Funktion, die für den clientseitigen Aufruf des Webdiensts zuständig ist. Sie übernimmt die folgenden vier Argumente, von denen zwei optional sind:-
methodName – Der Name der Methode, die für den Webdienst auf aufruft wird.
-
dataSetName – Der Name des DataSet-Eintrags, der vom Webdienst zurückgegeben wird.
-
wsParamValue – Der Wert des Parameters, der ggf. an den Webdienst übergeben wird. (Optional)
-
wsParamName – Der Name des Parameters, der ggf. an den Webdienst übergeben wird. (Optional)
Unterteilen wir die getDataFromWS-Funktion in Teile und besprechen die einzelnen Teile. Hier ist der erste Codeausschnitt:
// create the XML object
objXmlDoc = new ActiveXObject("Msxml2.DOMDocument"); if (objXmlDoc == null) { alert("Unable to create DOM document!"); } else { // create an XMLHTTP instance objHttp = new ActiveXObject("Microsoft.XMLHTTP");
Dieser Codeblock erstellt das XMLHTTP-Objekt und das XML-Dokument-Objekt. Als Nächstes beginne ich mit der Erstellung des SOAP-Umschlags.
// Create the SOAP Envelope
strEnvelope = "<soap:Envelope xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + " xsd=\"http://www.w3.org/2001/XMLSchema\"" + " soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" + " <soap:Body>" + " <" + methodName + " xmlns=\"http://jimcoaddins.com/DynaProducts\">" + " </" + methodName + ">" + " </soap:Body>" + "</soap:Envelope>";
In diesem Code weist ich den SOAP-Umschlag einer Zeichenfolgenvariablen zu, damit ich ihn an den Webdienst weiterleite. Tatsächlich ist es ganz einfach zu erkennen, wie Sie den SOAP-Umschlag für Ihren Webdienst formatieren. Navigieren Sie einfach zum Webdienst, und klicken Sie auf eine der Methoden, um einen SOAP-Umschlag für diese Methode zu sehen. So sehe ich beispielsweise beim Browsen zur "GetCategories"-Methode des wsXMLHTTP-Webdiensts, den ich für diesen Artikel erstellt habe:ASP.NET teilt Ihnen mit, wie der SOAP-Umschlag für einen HTTP POST und einen HTTP GET formatiert werden
soll. In dem in diesem Artikel dargestellten Beispiel wird HTTP POST verwendet. So weit so gut. Sehen wir uns nun den nächsten Codeabschnitt an.// Set up the post
objHttp.onreadystatechange = function(){ // a readyState of 4means we're ready to use thedata returned byXMLHTTP if (objHttp.readyState == 4) { // getthe return envelope varszResponse= objHttp.responseText; // loadthe return into an XML data island objXmlDoc.loadXML(szResponse); if (objXmlDoc.parseError.errorCode != 0) { var xmlErr =objXmlDoc.parseError; alert("You have error " + xmlErr.reason); } else { switch(dataSetName) { case "CategoriesDS": processCategory(); break; case "ProductsDS": processProducts(); break; case "ProductDetailDS": processProductDetails(); break; } }
Wenn eine Anforderung über XMLHTTP erfolgt, verwendet das XMLHTTP-Objekt eine readyState-Eigenschaft, um den Status der Anforderung nachverfolgt. Wenn alle Daten vom Webdienst zurückerkommen wurden, wird die "readyState"-Eigenschaft in den Wert "4" geändert. Mit der "onreadystatechange"-Eigenschaft für das XMLHTTP-Objekt können Sie eine Rückruffunktion einrichten, die aufgerufen wird, wenn sich die "readyState"-Eigenschaft ändert. Indem ich dafür sorge, dass die Daten vollständig empfangen wurden, kann ich weiterhin mit den Daten weiter handeln, bis ich bereit bin.
Sobald alle Daten empfangen wurden, erstelle ich mithilfe der "responseText"-Eigenschaft eine XML-Dateninsel mit der Antwort. Wie Sie wahrscheinlich wissen, liegt die Antwort von einem Webdienst im XML-Format vor. In diesem Fall gibt ich ein Microsoft ADO.NET DataSet zurück. Im nächsten Abschnitt dieses Codeblocks wird eine switch-Anweisung verwendet, um die entsprechende Funktion basierend auf dem Namen des Vom Webdienst zurückgegebenen DataSets auf aufruft. Ich werde mich später ausführlich mit dem Code für diese Funktionen eingehen lassen. Sehen wir uns nun den Code an, mit dem die XMLHTTP-Anforderung tatsächlich erfüllt wird.var szUrl;
szUrl = "http://dadatop/wsXmlHttp/DynaProducts.asmx/" + methodName; if (wsParamValue != null) { szUrl += "?" + wsParamName + "=" + wsParamValue; } // send the POST to the Web service objHttp.open("POST", szUrl, true); objHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); objHttp.send(strEnvelope);
Die Variable "szUrl" enthält die URL, die zum Aufrufen des Webdiensts aus Gründen der Klarheit verwendet wird. Dann habe ich eine if-Anweisung, die auf parameter, die als QueryString-Wert übergeben werden, fühlt. In Ihrer Umgebung können Sie die Parameter zum SOAP-Umschlag hinzufügen. Beides funktioniert ganz gut.
Die open-Methode des XMLHTTP-Objekts wird als Nächstes aufgerufen. Ich habe die ersten drei Argumente für die open-Methode verwendet. die Methode, die URL und einen booleschen Wert, der angibt, ob der Aufruf asynchron ist. Wichtig Wenn Sie wie hier einen asynchronen Aufruf ausführen, müssen Sie über die "onreadystatechanged"-Eigenschaft eine Rückruffunktion einrichten. Nachdem der Anforderungsheader für den Inhaltstyp festgelegt wurde, sende ich die Anforderung als SOAP-Umschlag mit der zuvor ausgefüllten Zeichenfolgenvariablen. Wir haben nun den code, der die XMLHTTP-Anforderung erstellt, durchgegangen. Sehen wir uns nun den Code an, der die Schnittstelle im Browser behandelt und die Antwort des Webdienstaufrufs behandelt. Zunächst sehen wir uns die Funktion an, die beim ersten Laden der Seite aufgerufen wird.function getCategories()
{ var func = "getDataFromWS('GetCategories', 'CategoriesDS')"; document.all.lblCategoryDropdown.innerText = "Please wait while data is retrieved..."; window.setTimeout(func, 1); }
Als Erstes erstelle ich in dieser Funktion eine Variable zum Speichern der Funktionssignatur für "getDataFromWS". Dies ist der Grund dafür, dass ich am Ende dieser Funktion "window.setTimeout" aufrufe, um die "getDataFromWS"-Funktion auf aufrufe. Der Zweck dieses Ansatzes besteht in der Anzeige des Status für den Benutzer, während auf den Abschluss des Webdienstaufrufs gewartet wird. Beachten Sie, dass der innere Text eines DIV geändert wird, um eine Meldung anzuzeigen, die besagt, dass Daten abgerufen werden. Anschließend verplane ich die getDataFromWS-Funktion über den Window.setTimeout-Aufruf und set sie so ein, dass sie in einer Millisekunde ausgeführt wird.
Verarbeiten der Webdienstantwort
Denken Sie daran, dass ich zuvor die "onreadystatechanged"-Eigenschaft zum Konfigurieren einer Rückruffunktion verwendet habe. Denken Sie auch daran, dass die Rückruffunktion eine switch-Anweisung enthält, die eine bestimmte Funktion basierend auf dem Namen des DataSet aufruft. In diesem Fall ist der Name des DataSets CategoriesDS. Daher wird die "processCategory"-Funktion von der Rückruffunktion aufgerufen. Sehen wir uns diese Funktion an, um zu sehen, wie das XML-DOM verwendet wird, um die Antwort des Webdiensts zu analysieren.
function processCategory()
{ // get an XML data island with the category data objNodeList = objXmlDoc.getElementsByTagName("Categories"); // add default value to the drop-down document.forms[0].drpCategory.options[0] = new Option("Select a Category", 0); // walk through the nodeList and populate the drop-down for (var i = 0; i < objNodeList.length; i++) { var dataNodeList; var textNode; var valueNode; dataNodeList = objNodeList[i].childNodes; valueNode = dataNodeList.item(0); textNode = dataNodeList.item(1); document.forms[0].drpCategory.options[i + 1] = new Option(textNode.text, valueNode.text); document.all.lblCategoryDropdown.innerText = "Select a Category:"; document.forms[0].drpCategory.style.visibility = "visible"; } }
Denken Sie daran, dass die getDataFromWS-Funktion XML aus der Antwort in das objXmlDoc-Objekt geladen hat. In der "processCategory"-Funktion übernähte ich diesen XML-Code und analysiert ihn, um das Dropdown "Category" zu füllen.das GetCategories.xml herunter. Laden Sie das WSXMLHTTP.exe paket jetzt herunter.Wenn Sie weitere Informationen zum Herunterladen von Microsoft Supportdateien erhalten möchten, klicken Sie auf die folgende Artikelnummer, um den Artikel in der Microsoft Knowledge Base zu lesen:
Als Erstes erstelle ich ein IXMLDOMNodeList-Objekt mithilfe eines Teils der XML-Antwort. Der DataSet, den ich vom Webdienstaufruf zurückgebe, wird als Diffgram zurückgegeben, und der einzige Teil dieser Antwort, an dem ich wirklich interessiert bin, sind die Daten aus der DataTable, die ich in das DataSet eingefügt habe. Dazu kann ich ein IXMLDOMNodeList-Objekt aus dem XML-Block erstellen, der die DataTable enthält. Wenn Sie sich den Code für den Webdienst betrachten, sehen Sie, dass ich eine Datentabelle mit dem Namen "Kategorien" erstelle und sie dem DataSet hinzustelle. Wenn die XML vom Webdienst zurückgegeben wird, ist das DataSet in einem <CategoriesDS>-Block enthalten, und jede Zeile aus der DataTable ist in separaten <Categories>-Blöcken enthalten, wie in der folgenden XML-Datei gezeigt. Die folgenden Dateien stehen im Microsoft Download Center zum Download bereit:Laden Sie119591 Abrufen von Microsoft-Supportdateien von Onlinediensten, die microsoft auf Viren überprüft hat. Dazu wurde die neueste Software zur Virenerkennung verwendet, die zum Zeitpunkt der Bereitstellung verfügbar war. Die Datei befindet sich auf Servern mit verstärkter Sicherheit, wodurch nicht autorisierte Änderungen an der Datei weitestgehend verhindert werden. Um den XML-Block, der diese DataTable enthält, zu erhalten, verwende ich den folgenden Code:
objNodeList = objXmlDoc.getElementsByTagName("Categories");
Dadurch wird ein IXMLDOMNodeList-Objekt zurückgegeben, das jeden <Categories> enthält. Anschließend durchlaufe ich diese Liste mit einer for-Schleife.
// walk through the nodeList and populate the drop-down
for (var i = 0; i < objNodeList.length; i++) { var dataNodeList; var textNode; var valueNode; dataNodeList = objNodeList[i].childNodes; valueNode = dataNodeList.item(0); textNode = dataNodeList.item(1); document.forms[0].drpCategory.options[i + 1] = new Option(textNode.text, valueNode.text); document.all.lblCategoryDropdown.innerText = "Select a Category:"; document.forms[0].drpCategory.style.visibility = "visible"; }
Ich weiß bereits, dass jeder <"Categories>"-Knoten zwei Knoten hat, die ich brauche: den Knoten <ID> und den Knoten <CategoryName>. Daher erstelle ich zuerst eine neue IXMLDOMNodeList und füllt sie mit den untergeordneten Knoten des <Categories> aus.
dataNodeList = objNodeList[i].childNodes;
Anschließend verwende ich die Elementmethode, um auf beide Knoten zu zugreifen, die ich zum Auffüllen des Dropdowns benötigen. Der erste Knoten enthält das Feld "CategoryID" aus der Datenbank, und der zweite Knoten enthält das Feld "CategoryName" aus der Datenbank. Ich erstelle ein neues Option-Objekt, set den Text auf "CategoryName", set den Wert auf "CategoryID" und füge ihn der Dropdownliste "drpCategory" hinzu. Der Code, der in den verbleibenden Funktionen verwendet wird, verwendet dieselbe Methode, um die erforderlichen Daten aus der XML-Antwort zu ziehen und Teile der Seite zu füllen.
Beachten Sie, dass es hier um kleine Datenmengen geht, denn die Verwendung des DOM ist eine hervorragende Möglichkeit, die benötigten Daten herausziehen. Wenn Sie mit einer großen Datenmenge zu tun haben, können Sie stattdessen XSLT verwenden.So sorgen Sie dafür, dass alles funktioniert
Nachdem ich nun die drei Details dazu behandelt habe, wie all das funktioniert, ist es an der Zeit, zu zeigen, wie Sie die enthaltenen Beispieldateien verwenden können, um zu sehen, wie das für Sie funktioniert.
Bereitstellen des Webdiensts
Wenn Sie den ASP.NET bereitstellen möchten, entzippen Sie einfach das angefügte Webdienstbeispiel auf der Stammwebsite des Webservers. Anschließend müssen Sie den Code für "DynaProducts.asmx" öffnen und die Verbindungszeichenfolge ändern. Zumindest müssen Sie das Kennwort für SA eingeben. Nachdem Sie diese Änderung vorgenommen haben, kompilieren Sie den Webdienst erneut.
Bereitstellen der HTML-Datei
Die HTML-Datei enthält eine Variable namens "szUrl", die eine URL des Webdiensts enthält. Sie finden diese Variable in der getDataFromWS-Funktion unten in der Funktion. Sie müssen dies in die URL für den Webdienst ändern, den Sie oben bereitgestellt haben.
Nachdem Sie sowohl den Webdienst als auch die HTML-Datei bereitgestellt haben, navigieren Sie zur HTML-Datei. Beim Laden wird die Dropdownliste "Kategorie" von der ersten XMLHTTP-Anforderung an den Webdienst ausgefüllt. Nachdem diese ausgefüllt wurde, wählen Sie eine Kategorie aus, um die nächste XMLHTTP-Anforderung zu starten, die das Dropdown "Produkte" auffüllt. Wenn Sie ein Produkt aus der Dropdownliste "Produkte" auswählen, wird eine Tabelle mit Daten zu diesem Produkt auffüllt. Beachten Sie, dass die Seite während einer dieser XMLHTTP-Anforderungen nicht zurück posten wird. Das ist das Schöne an XMLHTTP-Anforderungen. Wenn ich dies auf einer großen Seite getan hätte, hätte die Seite auch die Bildlaufposition beibehalten, ohne dass der Benutzer "blinkt". Wenn Sie mich fragen, sind das einige ziemlich leistungsfähige Dinge! Noch ein Punkt: In diesem Artikel habe ich XMLHTTP zum Abfragen eines Webdiensts verwendet. Ich könnte es genauso einfach verwenden, um eine Anforderung für eine ASPX- oder eine ASP-Seite zu erstellen. Die Möglichkeiten, wie Sie diese Technologie nutzen können, sind endlos. Ich hoffe, dass SIE XMLHTTP bei der zukünftigen Webanwendungsentwicklung hilfreich finden.Senden Sie wie immer Ideen zu Themen, die in zukünftigen Spalten oder in der Microsoft Knowledge Base behandelt werden sollen, mithilfe des Formulars "Fragen" ein.