Start
Unternehmen
ERP / PPS / Prozesse
Business Intelligence
Server-Technologien
Software-Technologien
Technologie-Beratung
Individual-Software
Produkte

Comelio GmbH
Goethestraße 34
D-13086 Berlin
Deutschland
Fon: 030-921019-85
Fax: 030-921019-89
info@comelio.com

Comelio GmbH
Rellinghauser Straße 10
D-45128 Essen
Deutschland
Fon: 0201-437517-0
Fax: 0201-437517-10
info@comelio.com

Comelio GmbH (Ecos)
Stiglmaierplatz/Dachauer Str. 37
D-80335 München
Deutschland
Fon: 089-2000154-90
Fax: 089-2000154-94
info@comelio.com

Comelio GmbH
Bahnhofstrasse 52
CH-8001 Zürich
Schweiz
Fon:
info@comelio.com

Comelio-Blog > MS SQL Server > Nachrichtenstruktur von Webservices

Nachrichtenstruktur

Das automatisch erstellte WSDL liefert zwar die Information, dass überhaupt XML zurückgeliefert wird, wenn nicht nur ein einfacher Rückgabewert einer Funktion die Antwort bildet, aber in XML Schema wird nicht etwa die SELECT…FOR XML-Anfrage ausgelesen und so ausgewertet, dass die Antwortstruktur auch in diesem Bereich automatisch generiert wird.

Kontakt

Anrede* Herr Frau
Vorname*
Nachname*
Firma
E-Mail*
Tel-Nr.
Bereich*
Freitext

Webservices mit komplexer Nachrichtenstruktur

Wie zuvor schon gesehen, liefert das automatisch erstellte WSDL zwar die Information, dass überhaupt XML zurückgeliefert wird, wenn nicht nur ein einfacher Rückgabewert einer Funktion die Antwort bildet, aber in XML Schema wird nicht etwa die SELECT…FOR XML-Anfrage ausgelesen und so ausgewertet, dass die Antwortstruktur auch in diesem Bereich automatisch generiert wird. Während man diese Aufgabe dem WSDL-Generator vielleicht noch zutrauen würde, ist es unmöglich, sich vorzustellen, dass die eingehende Frage-Struktur auch automatisch aus dem Algorithmus von Prozedur/Funktion ausgelesen wird. Für beide Nachrichtentypen muss der xml-Datentyp in seiner typisierten Form auftreten, sodass aus dem im MS SQL Server angemeldeten XML auch unmittelbar die Strukturen ausgelesen werden. Dies soll im folgenden Beispiel erläutert und vorgeführt werden.

Strukturen und Prozedur erstellen und veröffentlichen

Die Grundidee, die Nachrichtenstruktur von Anfrage und Antwort vorzugeben, ist sehr einfach und baut auf den bereits im XML Schema-Kapitel beschriebenen Techniken auf. Sofern man als Webservice nur Operationen anbietet, die über eine Reihe von auch zu Überladung führenden Parametern gestartet werden können, dann stellt sich das diesem Abschnitt zu Grunde liegende Problem überhaupt nicht. Wenn allerdings komplexe XML-Nachrichten erwartet und verschickt werden sollen, dann muss dem Webservice-Entwickler auf der Klientenseite diese Struktur ebenfalls mitgeteilt werden. Es liegt nur nahe, diese auch im WSDL-Dokument unterzubringen, wo ohnehin schon alle Nachrichtenstrukturen in XML Schema angegeben werden. Dazu ist lediglich notwendig, typisiertes XML für die verschiedenen Parameter einzusetzen.

Als Beispiel soll eine sehr einfache Produktbestellung verwendet werden, bei der lediglich die Nachrichtenstrukturen betrachtet werden und ansonsten alle über die Zerlegung und Erstellung der Nachricht hinausgehenden denkbaren T-SQL-Anweisungen ausgelassen werden. In einem ersten Schritt muss das XML Schema für die Produktbestellung (Anfrage) unter einem Namen im MS SQL Server angemeldet werden. Es enthält innerhalb eines ProductOrder-Elements die drei Kinder: die Produktnummer ProductNumber als xs:string, die Menge Quantity als xs:int und die Kontonummer AccountNumber als xs:string.

IF ((SELECT COUNT(*)
FROM sys.xml_schema_collections
WHERE [name] = 'ProductOrder') > 0) BEGIN
DROP XML SCHEMA COLLECTION Sales.ProductOrder
END
GO
CREATE XML SCHEMA COLLECTION Sales.ProductOrder AS
N'<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:c="http://www.contoso.com/ProductOrder" targetNamespace="http://www.contoso.com/ProductOrder">
<xs:element name="ProductOrder">
<xs:complexType>
<xs:sequence>
<xs:element name="ProductNumber" type="xs:string"/>
<xs:element name="Quantity" type="xs:int"/>
<xs:element name="AccountNumber" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>'
GO

Die Antwortnachricht kann bei längerem Überlegen überaus komplex ausfallen, denn eine wie auch immer einfach gehaltene Bestellung kann sich auf Produkte auswirken, die auf Lager sind und sofort verschickt werden sollen, oder auf solche, die noch extra bestellt und daher zu einem unbekannten Termin ausgeliefert werden können. Dazu könnten Produkte bestellt werden, die an bestimmte Kunden nicht geliefert werden können, die nur in bestimmten Mengen denkbar sind usw. Um wenigstens eine Fallunterscheidung zu verwenden, liefert die Antwortnachricht entweder eine Bestätigung oder eine Meldung zurück, dass das Produkt nicht lieferbar sei.

Im ersten Fall schickt es die eingegangenen Nachrichtenelemente als Bestätigung zurück und fügt ein Auslieferungsdatum ein. Die Meldung über die Nicht-Lieferbarkeit wird in einem Cancellation-Element untergebracht.

CREATE XML SCHEMA COLLECTION Sales.ProductOrderConfirmation AS
N'<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns="http://www.contoso.com/ProductOrderConfirmation" targetNamespace="http://www.contoso.com/
ProductOrderConfirmation">
<xs:element name="ProductOrderConfirmation">
<xs:complexType>
<xs:choice>
<xs:element name="Confirmation">
<xs:complexType>
<xs:sequence>
<xs:element name="ProductNumber"
type="xs:string"/>
<xs:element name="Quantity" type="xs:int"/>
<xs:element name="ShipDate" type="xs:string"/>
<xs:element name="AccountNumber"
type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Cancellation">
<xs:complexType>
<xs:sequence>
<xs:element name="Comment" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>'
GO

Um im nachfolgenden Quelltext nur die absolut notwendigen Bestandteile zu zeigen, wurden die Anforderungen hinsichtlich der Realitätstreue erheblich reduziert und auch eine Funktion für die Ermittlung der Menge von Produkten auf dem Lager namens int Sales.usfGetQuantity(@productNumber nvarchar(25)) erstellt. Sie ist ebenfalls in der Datei 521_01.sql ganz am Anfang vorhanden und muss vor einem Test des gesamten Beispiels zunächst erstellt werden. Sie ist nicht abgedruckt.

Nachdem die beiden XML Schema-Dateien übertragen sind, erstellt man in einem zweiten Schritt erst die Funktion, welche den Webservice bilden soll. Dabei verwendet man jeweils typisiertes XML für den einzigen Übergabeparameter und den Rückgabewert. Dies führt dazu, dass in der automatisch erstellten WSDL-Datei ebenfalls die zuvor übertragenen XML Schema-Angaben zu finden sind und sich ein Entwickler, der den Webservice nutzt, sich über die benötigten Strukturen informieren kann.
In einem dritten Schritt muss bei der Erstellung von Funktion/Prozedur, welche den Webservice bilden soll, das übergebene XML zerlegt werden, was mit den unterschiedlichen T-SQL-Mitteln durchgeführt werden kann. Die ausgelesenen Daten können dann beliebig verarbeitet werden. Im aktuellen Beispiel kann man sich vorstellen, dass wesentlich mehr Aktionen notwendig wären, als einfach nur die Verfügbarkeit des Produkts auf Lager zu testen.

In einem vierten und letzten Schritt setzt man schließlich eine Antwort-Nachricht über Zeichenketten-Verarbeitung oder eine Abfrage zusammen, welche an den Klienten wieder zurückgeliefert wird.

CREATE FUNCTION Production.ProductOrder ( 
@order xml(Sales.ProductOrder)
)
RETURNS xml(Sales.ProductOrderConfirmation)
AS BEGIN
-- XML-Spaltenvariablen, Hilfsvariablen
DECLARE @productNumber varchar(50),
@quantity int,
@accountNumber varchar(25),
@availableProducts int,
@confirmation xml(Sales.ProductOrderConfirmation)
-- Zerlegung
SET @productNumber = @order.value(
'declare namespace c="http://www.contoso.com/ProductOrder"; (//c:ProductNumber)[1]', 'varchar(25)')
SET @quantity = @order.value(
'declare namespace c="http://www.contoso.com/ProductOrder"; (//c:Quantity)[1]', 'int')
SET @accountNumber = @order.value(
'declare namespace c="http://www.contoso.com/ProductOrder"; (//c:AccountNumber)[1]', 'varchar(25)')
-- Erhältlichkeit prüfen?
SELECT @availableProducts =
Sales.usfGetQuantity(@productNumber)
-- Produkt erhältlich
IF @availableProducts > 0 BEGIN
-- Bestellung auslösen und Prozesse anstoßen...
SET @confirmation = N'<c:ProductOrderConfirmation xmlns:c="http://www.contoso.com/ProductOrderConfirmation">
<c:Confirmation>
<c:ProductNumber>BA-8327</c:ProductNumber>
<c:Quantity>3</c:Quantity>
<c:ShipDate></c:ShipDate>
<c:AccountNumber>AW00019908</c:AccountNumber>
</c:Confirmation>
</c:ProductOrderConfirmation>'
END
-- Produkt nicht erhältlich
ELSE
SET @confirmation = N'<c:ProductOrderConfirmation xmlns:c="http://www.contoso.com/ProductOrderConfirmation">
<c:Cancellation>
<c:Comment>Produkt nicht verfügbar.</c:Comment>
</c:Cancellation>
</c:ProductOrderConfirmation>'
RETURN @confirmation
END

Wie man unschwer erkennen kann, nimmt die Erstellung eines solchen Webdienstes deutlich mehr Zeit in Anspruch, als man es vielleicht bei den ersten Gehversuchen mit einfachen Funktionen/Prozeduren vermuten konnte. Weil man zunächst auch erst XML Schema-Strukturen anmelden muss, auf die dann nachher verwiesen wird, erhält man zudem ein sehr umfangreiches Geflecht an Strukturen in der Datenbank, das am besten nach Erstellung der Funktion/Prozedur, welche den Webservice später abbilden soll, getestet wird. Da man sich innerhalb von T-SQL bewegt, ist es auch nicht notwendig, unbedingt den gesamten Webservice anzumelden und in .NET einen Test-Klienten zu schreiben. Vielmehr kann man auch einfach nur in T-SQL die eigentliche Funktion/Prozedur überprüfen, ob bei Übergabe der erwarteten XML-Anfrage auch die gewünschte XML-Antwort zurückkommt.

SOAP-Nachrichten

Die beiden Dateien 521_01.wsdl und 521_01simple.wsdl enthalten die beiden WSDL-Dateien für diesen Dienst. Der WSDL-Generator fügt den zuvor angemeldeten XML Schema-Strukturen noch automatisch Namensraum-Informationen hinzu, verzichtet allerdings ansonsten bei der Standard-WSDL-Datei auf die Verkürzung, welche durch das xs:any-Element bewirkt wird. Hier erscheinen also in der Webservice-Beschreibung ausdrücklich die benötigten und zurückgelieferten XML-Strukturen. Lediglich die vereinfachte Variante beschreibt die Nachrichten nur mit <xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" />.

Im allerersten Beispiel hatte man schon gesehen, wie eine Antwort-Nachricht eine komplexe XML-Nachricht enthält. Dies ist bei dem gerade erstellten Webservice nun auch bei der Anfrage der Fall. Hier verschickt man eine ähnliche Datei wie zuvor für den Test und fragt, ob die erstellte Funktion die übergebene XML-Datei überhaupt akzeptiert, verarbeiten und eine sinnvolle Antwort zurückliefern kann.

<SOAP-ENV:Envelope 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<t:ProductOrder xmlns:t="http://tempUri.org/">
<t:order>
<c:ProductOrder
xmlns:c="http://www.contoso.com/ProductOrder">
<c:ProductNumber>BA-8327</c:ProductNumber>
<c:Quantity>3</c:Quantity>
<c:AccountNumber>AW00019908</c:AccountNumber>
</c:ProductOrder>
</t:order>
</t:ProductOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Sofern das Produkt tatsächlich verfügbar ist, erhält man innerhalb der beiden Element ProductOrderResponse, das die Operation beschreibt, und ProductOrderResult, welches das gesamte Ergebnis umschließt, das Element Confirmation mit der im XML Schema angegebenen Kind-Struktur. Weil das Lieferdatum bei der Erstellung nicht weiter beachtet worden ist, ist es nun leer.

...
<c:ProductOrderConfirmation xmlns:c="http://
www.contoso.com/ ProductOrderConfirmation">
<c:Confirmation>
<c:ProductNumber>BA-8327</c:ProductNumber>
<c:Quantity>3</c:Quantity>
<c:ShipDate/>
<c:AccountNumber>AW00019908</c:AccountNumber>
</c:Confirmation>
</c:ProductOrderConfirmation>
...

Im gegenteiligen Fall erhält man die Antwort im Cancellation-Element mit der zuvor angegebenen Meldung, dass das Produkt nicht verfügbar sei. Ob dies nun eine Fehlermeldung oder doch eher eine tatsächliche Antwort zu einer Bestellung ist, die nach einem nicht lieferbaren Produkt fragte, kann man diskutieren.

...
<c:ProductOrderConfirmation xmlns:c="
http://www.contoso.com/ProductOrderConfirmation">
<c:Cancellation>
<c:Comment>Produkt nicht verfügbar.</c:Comment>
</c:Cancellation>
</c:ProductOrderConfirmation>
</method:ProductOrderResult>
</method:ProductOrderResponse>
...

In der Datei soap-anfrage-fehler.xml befindet sich dagegen ein tatsächlicher XML-Fehler; nämlich das leere Element <c:ProductOrder xmlns:c="http:// www.contoso.com/ProductOrder"/>. In der nachfolgenden Datei fehlen einige Elemente und alle Namensräume, um sie möglichst kurz zu halten.

<SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>There was an error in the incoming SOAP request packet: Client, InvalidXml</faultstring>
<detail>
<SOAP-1_2-ENV:Detail>
<sqlresultstream:SqlMessage>
<sqlmessage:Class>16</sqlmessage:Class>
<sqlmessage:LineNumber>1</sqlmessage:LineNumber>
<sqlmessage:Message>XML-Überprüfung: Ungültiger Inhalt. Erwartete Elemente: http://www.contoso.com/ ProductOrder:ProductNumber. Ort: /*:ProductOrder[1].
</sqlmessage:Message>
</sqlresultstream:SqlMessage>
</SOAP-1_2-ENV:Detail>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Bei der Erstellung des Klienten erscheint dieses Mal in der Vorschau des ausgewählten Webservices der Eintrag ProductOrder() AS ProductOrderType unter METHODEN, da nun die XML-Nachricht als Klasse abgebildet wird. Sie wird vom Visual Studio automatisch erstellt.

Ein Klient in .NET

Bei einer Übergabe von XML aus einer .NET-Klientanwendung an den MS SQL Server wird der Vorteil, dass der xml-Datentyp der Datenbank auch fragmentiertes XML, d.h. XML ohne Wurzelelement, speichern kann, zu einem Nachteil für den Klienten. Die Instanzen des Datentyps xml werden im Visual Studio einem Array von System.Xml.XmlNode zugeordnet und können nicht als System.Xml.XmlDocument direkt abgerufen werden. Die letztere Möglichkeit wäre natürlich viel einfacher als das, was im nächsten Beispiel-.NET-Programm präsentiert wird.

Um den Quelltext abzukürzen, wird der XML-Quelltext, der zum Webservice geschickt wird, nicht als DOM-Dokument erstellt oder aus einer Datei abgerufen, sondern direkt als (gekürzte) Zeichenkette fest gespeichert. Man kann sich allerdings sicherlich gut vorstellen, aus wie vielen unterschiedlichen Quellen das XML direkt in der korrekten Form abgerufen, aus einer anderen Form mit XSLT/DOM umgewandelt oder aus einem Formular in XML zusammengesetzt werden könnte. Ebenso kann man sich sicherlich leicht vorstellen, wie das vom Webservice übermittelte XML-Antwort-Dokument mit XSLT in XML/HTML umgewandelt oder mit dem DOM für ein Formular in einzelne Variablen zerlegt wird.

Da der xml-Datentyp fragmentiertes XML unterstützt, muss in .NET ebenfalls ein XmlDocumentFragment erstellt werden. Es besitzt ebenfalls eine Outer-/InnerXml-Eigenschaft, die man zum Schreiben und Lesen von XML-Daten verwenden kann. Dabei erstellt man zunächst ein Array von XmlNode-Objekten, das nur aus einem einzigen Knoten besteht, in dem man seine abgerufenen XML-Daten speichern und schließlich dem Parameterobjekt für die XML-Nachricht zuweisen kann. Die Verwendung des Webservices selbst entspricht in diesem Beispiel dem vorherigen .NET-Klienten. Hier kommen nur ganz andere Datentypen zum Einsatz.

Die beiden Eigenschaften OuterXml und InnerXml besitzen unterschiedliche Daten, die insbesondere für die Antwortnachricht bedeutsam sind. InnerXml enthält ausschließlich die über XML Schema modellierte Antwortnachricht, d.h. das Elternelement Confirmation. Die Eigenschaft OuterXml enthält dagegen auch noch das umschließende Element für den Operationsnamen ProductOrderConfirmation. Sofern hier also eine XML Schema-Validierung für die Antwort durchgeführt werden soll, ist die InnerXml-Eigenschaft die bessere Wahl, da hier nicht noch zusätzlich das Eltern-Element überspringen werden muss.

static void Main(string[] args) {
// XML-Bestellung als Zeichenkette (kürzer als DOM)
String orderText = "<c:ProductOrder ...</c:ProductOrder>";
// Bestellung in automatisch erstelltem Typ anlegen
ProductOrderTypeorder order = new ProductOrderTypeorder();
// Zeichenktte in XML umwandeln
XmlDocument xmldoc = new XmlDocument();
XmlDocumentFragment fragIn = xmldoc.
CreateDocumentFragment();
fragIn.InnerXml = orderText;
XmlNode[] xmlnodes = new XmlNode[1];
xmlnodes[0] = (XmlNode)fragIn;
// Bestellung XML zuweisen
order.Any = xmlnodes;
// Webservice-Proxy anlegen
epProduct proxy = new epProduct();
proxy.Credentials = System.Net.CredentialCache.
DefaultCredentials;
// Webservice aufrufen und XML-Bestellung verschicken
try {
// Bestätigung in automatisch erstelltem Typ anlegen
ProductOrderType confirmation = proxy.
ProductOrder(order);
// XML als Zeichenkette auslesen und ausgeben
XmlNode[] nodeArr = confirmation.Any;
String confirmationXml = nodeArr[0].InnerXml;
String soapXml = nodeArr[0].OuterXml;
Console.WriteLine(confirmationXml);
Console.WriteLine("-----------------");
Console.WriteLine(soapXml);
}
...

Als Ergebnis des Programms erhält man in der Kommandozeile die übermittelte Antwort, wobei beide möglichen XML-Fassungen angezeigt werden.

    Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik Comelio GmbH MS SQL Server: SOAP-Nachrichtenstruktur - T-SQL XML Webservices Programmierung Bücher Anleitung Tutorial Skulschus Wiederstein Kozik