![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Предисловие Настройка IIS Содержание wsdl-файла Создание Web-методов Тестируем Web-сервис Поддержка SQLXML в .NET Framework Тестовый проект Заключение Литература | ![]() |
Эту статью я собирался написать очень давно. Около года прошло с момента появления первой части «Использование XML совместно с SQL», а это – очень большой срок для современной IT-индустрии. Сейчас уже доступна первая бета новой версии SQL Server-а под кодовым названием «Yukon». В нем будет просто сумасшедшее количество нововведений, связанных с XML (и не только), одним из которых является возможность напрямую обращаться к серверу через Internet. Как вы знаете, в текущей версии это невозможно: взаимодействие с сервером происходит через специальное ISAPI-расширение, которое является частью пакета SQLXML. Вообще, по сравнению с Yukon (или Oracle9i), поддержка XML в SQL Server 2000 очень слаба. По существу, она ограничивается двумя конструкциями: FOR XML и OPENXML. Вся остальная функциональность реализуется с помощью SQLXML[1]. Все, что предоставляет SQLXML, как-то: шаблоны, запросы в URL, запросы XPath в конечном счету превращаются в ту или иную форму SQL-запроса, вида:
select ... from [table] ... for xml explicit |
Вызов хранимых процедур и функций через Internet – также целиком и полностью заслуга SQLXML: с помощью ISAPI-расширения он перехватывает SOAP-запросы, транслирует их в вызовы соответствующих функций и процедур SQL Server-а и возвращает результат в виде XML. Трансформация выбранного recordset-а в XML происходит на основе информации конфигурационного файла (расширение ssc), но обо всем по порядку.
Для того чтобы стал возможен вызов хранимых процедур и функций как Web-методов, необходимо создать и настроить виртуальную директорию Internet-сервера. Это выполняется с помощью консоли «IIS Virtual Directory Management for SQLXML 3.0». Я не буду рассматривать все шаги этого процесса, так как для всех типов запросов (soap, templates, dbobject и schema) он одинаков. Подробную информацию об этом можно получить из документации по SQLXML 3.0 и в [1]. Рассмотрим лишь, как настроить запросы типа soap. На рисунке 1 приведен пример.
Рисунок 1. Создание Web-сервиса.
Здесь имя виртуальной директории – «srv». Имя Web-сервиса – webserv, тип директории – soap, имя хоста – dcit06. Кроме этого, необходимо указать физический путь до папки, где будут располагаться конфигурационный файл и файл описания Web-сервиса. После того, как вы нажмете кнопку «Save», SQLXML создаст wsdl-файл, в котором будет содержаться описание Web-сервиса, и конфигурационный файл ssc, в котором будут содержаться описания всех Web-методов и их параметров. Файл ssc предназначен только для внутренних служебных нужд SQLXML, его формат нигде не описан и в любой момент может измениться.
На вкладке Settings не забудьте поставить галочку напротив опции «Allow POST».
WSDL – это специальный формат, предназначенный для описания Web-сервисов, их методов, параметров и используемых протоколов. Это обычный XML-документ с корневым тегом definitions, содержащий описания типов, параметров, операций, протоколов и сервисов. Они подробно будут описаны далее. SQLXML генерирует файл в формате WSDL 1.1, который имеет статус ноты (как и SOAP 1.1).
ПРИМЕЧАНИЕ Если вы не знакомы с терминологией наименования этапов обработки документов консорциума w3c, рекомендую ознакомиться с документом http://www.w3.org/Consortium/Process. |
В данный момент уже практически завершается работа по созданию спецификации WSDL 1.2. Более подробную информацию можно получить по адресу http://www.w3.org/2002/ws.
Файл WSDL условно можно поделить на две части: абстрактную и конкретную часть. Первая описывает типы, параметры и операции и не касается конкретных протоколов и конечных точек (endpoints) или сервисов. WSDL предназначен не только для работы по протоколу SOAP, так что описание конкретного транспорта выделается в отдельные, конкретные элементы, описание которых находится в второй части WSDL файла. Рассмотрим по порядку фрагменты абстрактной и конкретной частей, которые далее в статье будут называться секциями.
Эта секция содержит набор схем в формате XML Schema, каждая из которых описывает абстрактные типы, не зависящие от какого-либо языка или машины. Эта секция не является обязательной и может быть опущена, если Web-сервис не нуждается в дополнительных типах (как правило, это составные типы – complex types) и вполне обходится стандартными, определенными в XML Schema.
SQLXML определяет четыре схемы типов:
http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types. Эта схема описывает базовые типы, используемые при построении сообщений.
Простой тип nonNegativeInteger – целочисленный тип без знака в диапазоне от 0 до 2147483647.
Атрибут IsNested булевого типа.
Сложный тип SqlRowSet – используется в случае, когда результирующее сообщение имеет тип DataSet object. Более подробно о типах результирующих сообщений см. раздел «Создание Web-методов».
Сложный тип SqlXml – используется в случае, когда результирующее сообщение имеет тип XML objects.
Простой тип SqlResultCode - целочисленный тип без знака в диапазоне от 0 до 2147483647, который используется для описания возвращаемого хранимой процедурой значения.
http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlMessage. Эта схема определяет единственный тип, который описывает сообщение от SQL Server-а в случае ошибки.
Сложный тип SqlMessage – содержит поля для описания кода ошибки, номера строки, строкового представления ошибки и других параметров.
http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlResultStream. Эта схема определяет единственный сложный тип, который описывает любой ответ от сервера.
Сложный тип SqlResultStream – содержит одно и более вхождений полей следующих типов: SqlRowSet, SqlXml, SqlMessage и SqlResultCode.
Пространство имен конкретного Web-сервиса, которое импортирует все вышеперечисленные пространства имен и описывает типы, необходимые конкретным Web-методам. В данный момент эта схема не содержит ни одного описания, так как пока Web-сервис не определил каких-либо Web-методов.
В качестве примера приведу фрагмент WSDL-файла, сгенерированного при создании виртуальной директории.
<wsdl:types> <xsd:schema targetNamespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types" elementFormDefault="qualified" attributeFormDefault="qualified"> <xsd:import namespace="http://www.w3.org/2001/XMLSchema"/> <xsd:simpleType name="nonNegativeInteger"> <xsd:restriction base="xsd:int"> <xsd:minInclusive value="0"/> </xsd:restriction> </xsd:simpleType> <xsd:attribute name="IsNested" type="xsd:boolean"/> <xsd:complexType name="SqlRowSet"> <xsd:sequence> <xsd:element ref="xsd:schema"/> <xsd:any/> </xsd:sequence> <xsd:attribute ref="sqltypes:IsNested"/> </xsd:complexType> <xsd:complexType name="SqlXml" mixed="true"> <xsd:sequence> <xsd:any/> </xsd:sequence> </xsd:complexType> <xsd:simpleType name="SqlResultCode"> <xsd:restriction base="xsd:int"> <xsd:minInclusive value="0"/> </xsd:restriction> </xsd:simpleType> </xsd:schema> <xsd:schema targetNamespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlMessage" elementFormDefault="qualified" attributeFormDefault="qualified"> <xsd:import namespace="http://www.w3.org/2001/XMLSchema"/> <xsd:import namespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types"/> <xsd:complexType name="SqlMessage"> <xsd:sequence minOccurs="1" maxOccurs="1"> <xsd:element name="Class" type="sqltypes:nonNegativeInteger"/> <xsd:element name="LineNumber" type="sqltypes:nonNegativeInteger"/> <xsd:element name="Message" type="xsd:string"/> <xsd:element name="Number" type="sqltypes:nonNegativeInteger"/> <xsd:element name="Procedure" type="xsd:string"/> <xsd:element name="Server" type="xsd:string"/> <xsd:element name="Source" type="xsd:string"/> <xsd:element name="State" type="sqltypes:nonNegativeInteger"/> </xsd:sequence> <xsd:attribute ref="sqltypes:IsNested"/> </xsd:complexType> </xsd:schema> <xsd:schema targetNamespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlResultStream" elementFormDefault="qualified" attributeFormDefault="qualified"> <xsd:import namespace="http://www.w3.org/2001/XMLSchema"/> <xsd:import namespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types"/> <xsd:import namespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlMessage"/> <xsd:complexType name="SqlResultStream"> <xsd:choice minOccurs="1" maxOccurs="unbounded"> <xsd:element name="SqlRowSet" type="sqltypes:SqlRowSet"/> <xsd:element name="SqlXml" type="sqltypes:SqlXml"/> <xsd:element name="SqlMessage" type="sqlmessage:SqlMessage"/> <xsd:element name="SqlResultCode" type="sqltypes:SqlResultCode"/> </xsd:choice> </xsd:complexType> </xsd:schema> <xsd:schema targetNamespace="http://dcit06/srv/webserv" elementFormDefault="qualified" attributeFormDefault="qualified"> <xsd:import namespace="http://www.w3.org/2001/XMLSchema"/> <xsd:import namespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types"/> <xsd:import namespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlMessage"/> <xsd:import namespace="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlResultStream"/> </xsd:schema> </wsdl:types> |
Эта секция содержит описания параметров Web-методов. В текущем WSDL-файле ее нет, так как мы пока еще не определили никакого метода. Вообще, секция message состоит из набора частей (parts), каждая из которых описывает конкретный параметр. Часть может быть и одна, в этом случае используется несколько отличный синтаксис, при котором задается конкретный элемент части, вместо перечисления нескольких типов. Ниже приведен пример одинаковых параметров.
<wsdl:message name="mes1"> <wsdl:part name="param1" type="xsd:int"/> <wsdl:part name="param2" type="xsd:int"/> </wsdl:message> |
<wsdl:types> <xsd:schema targetNamespace="http://tempuri.org" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://tempuri.org"> <xsd:complexContent name="params"> <xsd:sequence> <xsd:element name="param1" type="xsd:int"/> <xsd:element name="param2" type="xsd:int"/> </xsd:sequence> </xsd:complexContent> </xsd:schema> </wsdl:types> <wsdl:message name="mes1"> <wsdl:part name="params" type="tns:param"/> </wsdl:message> |
<wsdl:types> <xsd:schema targetNamespace="http://tempuri.org" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://tempuri.org"> <xsd:element name="params"> <xsd:complexContent> <xsd:sequence> <xsd:element name="param1" type="xsd:int"/> <xsd:element name="param2" type="xsd:int"/> </xsd:sequence> </xsd:complexContent> </xsd:element> </xsd:schema> </wsdl:types> <wsdl:message name="mes1"> <wsdl:part name="params" element="tns:params"/> </wsdl:message> |
В SQLXML принят последний стиль описания параметров, так как в нем используется документ-литеральная форма SOAP-сообщений. О формах сообщений мы поговорим чуть позже.
Последняя секция, где описываются абстрактные параметры Web-сервиса. Эта секция является обязательной и содержит неопределенное количество необязательных разделов operation. Операции описывают конкретные Web-методы. Так как у нас пока нет Web-методов, то и количество разделов operation равно нулю.
У каждой операции есть имя и набор входных, выходных и ошибочных параметров, представленных элементами input, output и fault соответственно. Входные и выходные параметры могут образовывать различные типы операций: запрос/ответ, запрос, ответ и ответ/запрос. Это достигается с помощью соответствующей комбинации элементов input и output. Например, следующий псевдофрагмент описывает схему взаимодействия запрос/ответ (вопросительный знак обозначает необязательность элемента, звездочка – неопределенное количество элементов):
<wsdl:portType .... > <wsdl:operation name="nmtoken" parameterOrder="nmtokens"> <wsdl:input name="nmtoken"? message="qname"/> <wsdl:output name="nmtoken"? message="qname"/> <wsdl:fault name="nmtoken" message="qname"/>* </wsdl:operation> </wsdl:portType > |
Спецификация SOAP старше WSDL. На момент ее разработки и утверждения у программистов не было механизмов описания Web-сервисов, которые можно было бы использовать без предварительного изучения протокола. Для обхода этой проблемы спецификация SOAP определяет специальный формат обмена сообщениями, известный как RPC/encoded. RPC – определяет форму сообщения, а encoded – методы сериализации данных в тело SOAP-сообщения. Encoded сериализация подробно описана в разделе 5 спецификации [6].
С выходом WSDL появилась возможность описывать формат сообщений с помощью XML Schema. Эта форма получила название document/literal. Document означает, что тело SOAP-сообщения может и не представлять собой вызов метода. Это может быть просто набор элементов, соответствующих какому-либо бизнес-документу. Literal означает, что формат сообщения полностью определяется секцией types, т.е. схемой документа. Никаких особых правил, как в случае encoded сериализации, здесь не применяется.
Возможны также и комбинации этих форматов: RPC/literal, document/encoded. Рассмотрим их по порядку.
RPC-стиль подразумевает, что сообщение содержит только один элемент верхнего уровня (непосредственный дочерний элемент SOAP:Body), название которого соответствует имени удаленного метода. Имя метода определяется именем элемента operation секции portType WSDL-документа. Пространство имен этого элемента определяется значением атрибута namespace элемента связывания SOAP:Body. Каждый вложенный в него элемент представляет параметр метода, имя которого определяется именем части в секции message WSDL-документа. Таким образом, частей может быть несколько, так как их количество равно количеству параметров метода. Элемент, представляющий параметр, должен быть неквалифицированным, т.е. не должен принадлежать никакому или пустому пространству имен. Содержимое элементов, представляющих параметры, определяется типом сериализации: encoded или literal.
Document стиль определяет, что сообщение будет в точности соответствовать схеме, описанной в разделе types. Количество элементов верхнего уровня может быть любым. Имя элемента верхнего уровня определяется схемой, а не именем операции, как в RPC-стиле. Количество частей, ссылающихся на тип в разделе message – не более одной. Если часть ссылается на элемент, их может быть несколько. Содержимое элементов, представляющих параметры, определяется типом сериализации: encoded или literal.
Encoded-сериализация слишком сложна и объемна для этой статьи. Интересующихся отсылаю к разделу 5 спецификации [6]. Что касается WSDL-документа, то используя этот тип сериализации, вы не можете указывать конкретных элементов в части секции message. Т.е. допустим только синтаксис указания типа.
<message name="fooIn"> <part name="i" type="xsd:int" /> </message> <message name="fooOut" /> <portType name="Port1"> <operation name="foo"> <input message="fooIn" /> <output message="fooOut" /> </operation> </portType> |
Тело SOAP сообщения
<SOAP-ENV:Body> <m:foo xmlns:m="(TargetNamespace)" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <i xsi:type="xsd:int">0</i> </m:foo> </SOAP-ENV:Body> |
<message name="fooIn"> <part name="i" type="xsd:int"/> </message> <message name="fooOut"/> <portType name="Port1"> <operation name="foo"> <input message="fooIn" /> <output message="fooOut" /> </operation> </portType> |
Тело SOAP-сообщения
<SOAP-ENV:Body> <SOAP- ENC:int>0</SOAP- ENC:int > </SOAP-ENV:Body> |
Literal-сериализация намного проще encoded, так как содержимое документа определяется только схемой. Части сообщения могут быть представлены либо одним типом, либо набором элементов. В первом случае, само содержимое элемента soap:body кодируется в соответствии с типом части, а во втором случае – тело SOAP-сообщения содержит набор описанных элементов.
<s:schema> <s:element name="str" type="xsd:string"/> <s:element name="int" type="xsd:int"/> </s:schema> <message name="fooIn"> <part name="s" element="m:str" /> <part name="i" element="m:int" /> </message> <message name="fooOut"> <part name="retval" type="xsd:boolean" /> </message> <portType name="Port1"> <operation name="foo"> <input message="fooIn" /> <output message="fooOut" /> </operation> </portType> |
Тело SOAP сообщения
<SOAP-ENV:Body>
<m:foo xmlns:m="(TargetNamespace)">
<m:str>some string</m:str>
<m:int>0</m:int>
</m:foo>
</SOAP-ENV:Body>
|
<s:schema> <s:element name="some_document"> <s:complexType name="unimportant"> <s:sequence> <s:element name="str" type="xsd:string"/> <s:element name="int" type="xsd:int"/> </s:sequence> </s:complexType> </s:element> </s:schema> <message name="fooIn"> <part name=" unimportant " element="m:some_document" /> </message> <message name="fooOut"> <part name="retval" type="xsd:boolean" /> </message> <portType name="Port1"> <operation name="foo"> <input message="fooIn" /> <output message="fooOut" /> </operation> </portType> |
Тело SOAP-сообщения
<SOAP-ENV:Body> <m:some_document> <m:str>some string</m:str> <m:int>0</m:int> </m:some_document> </SOAP-ENV:Body> |
<s:schema> <s:complexType name="unimportant"> <s:sequence> <s:element name="str" type="xsd:string"/> <s:element name="int" type="xsd:int"/> </s:sequence> </s:complexType> </s:schema> <message name="fooIn"> <part name="unimportant" type="m:unimportant" /> </message> <message name="fooOut"> <part name="retval" type="xsd:boolean" /> </message> <portType name="Port1"> <operation name="foo"> <input message="fooIn" /> <output message="fooOut" /> </operation> </portType> |
Тело SOAP-сообщения
<SOAP-ENV:Body> <m:str>some string</m:str> <m:int>0</m:int> </SOAP-ENV:Body> |
Стили и способы сериализации задаются в секции binding WSDL-документа (см. ниже).
Стиль document/literal постепенно вытесняет остальные стили, так как он намного проще в понимании и реализации. Кроме этого, этот стиль позволяет точно определить содержимое документа по его схеме, тогда как при использовании стиля RPC/encoded нужно знать некоторые правила сериализации.
ПРИМЕЧАНИЕ В новой спецификации SOAP 1.2, которая уже стала рекомендацией, правила RPC-сериализации несколько упрощены, однако они все равно уступают литеральной сериализации по простоте. |
Visual Studio.NET для проекта ASP.NET Web-service по умолчанию генерирует именно стиль document/literal, а .Net Remоting придерживается, наоборот, RPC/encoded. В конечном счете, и там и там можно настроить стиль под собственные нужды, так что выбор остается за вами.
Здесь описывается привязка конкретных операций (Web-методов) к конкретному протоколу и стилю вызова (для SOAP это может быть document- или RPC-стиль). Так как сам WSDL является абстрактным форматом, он не имеет средств для определения зависимых от протокола параметров, его синтаксис позволяет вставлять в определенные места фрагмента binding элементы расширения для конкретного протокола. Вот фрагмент секции binding, взятый из спецификации WSDL:
<wsdl:binding name="nmtoken" type="qname"> * <-- extensibility element (1) --> * <wsdl:operation name="nmtoken"> * <-- extensibility element (2) --> * <wsdl:input name="nmtoken"? > ? <-- extensibility element (3) --> </wsdl:input> <wsdl:output name="nmtoken"? > ? <-- extensibility element (4) --> * </wsdl:output> <wsdl:fault name="nmtoken"> * <-- extensibility element (5) --> * </wsdl:fault> </wsdl:operation> </wsdl:binding> |
Как видите, элементы расширения могут появляться во всех частях связывания, определяя поведение всего связывания (1), конкретной операции (2), входных (3), выходных (4) и ошибочных (5) параметров.
Рассмотрим для примера элементы расширения для протокола SOAP.
<soap:binding style="rpc|document" transport="uri"> – элемент расширения связывания, который определяет стиль SOAP-сообщения и транспорт. Расширение является обязательным.
<soap:operation soapAction="uri"? style="rpc|document"?>? – элемент расширения операции, который определяет элемент заголовка soapAction и переопределяет стиль для конкретного сообщения. Необязательное расширение.
4. и 5. <soap:body parts="nmtokens"? use="literal|encoded"? encodingStyle="uri-list"? namespace="uri"?> – элемент расширения параметров.
В нашем случае эта часть WSDL-файла будет выглядеть так:
<wsdl:binding name="SXSBinding" type="tns:SXSPort"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> </wsdl:binding> |
Здесь определяется стиль SOAP-сообщения document и транспорт http. Операции, как видите, отсутствуют.
Эта последняя секция, которая определяет набор портов для связываний. Порт – это абстрактное понятие конечной точки, которое уточняется с помощью опять же элементов расширения. В случае SOAP элемент расширения задает адрес Web-сервиса. Вот как выглядит этот фрагмент в нашем случае:
<wsdl:service name="webserv"> <wsdl:port name="SXSPort" binding="tns:SXSBinding"> <soap:address location="http://dcit06/srv/webserv" /> </wsdl:port> </wsdl:service> |
Более подробную информацию о WSDL 1.1, элементах расширения, вложениях и проч. можно получить по адресу http://www.w3.org/TR/wsdl.
Давайте попробуем создать Web-метод и обратиться к SQL Server через Web. Допустим, вы настроили Web-сервис на базу Northwind. На вкладке «Virtual Names» консоли «IIS Virtual Directory Management for SQLXML 3.0» выберите из списка webserv и нажмите кнопу «Configure». Появится диалоговое окно, изображенное на рисунке 2.
Рисунок 2. Создание Web-метода.
Более всего здесь интересен раздел «Output as», который позволяет настроить формат результирующего сообщения. Вне зависимости от этих настроек формат SOAP-конверта (soap envelope) будет таким:
<?xmlversion="1.0"encoding="utf-16"?> <SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sqltypes="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types" xmlns:sqlmessage="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlMessage" xmlns:sqlresultstream="http://schemas.microsoft.com/SQLServer/2001/12/SOAP/types/SqlResultStream" xmlns:tns="uri Web-сервиса"> <SOAP-ENV:Body> <tns:ИмяМетодаResponse> <!-- Содержимое зависит от настроек (опции Output as) --> </tns:ИмяМетодаResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
Далее описываются каждый из типов опции «Output as» и их влияние на содержимое SOAP-конверта.
В случае указания этот параметра возвращаемый набор рекордсетов будет представлен элементами SqlXml. После набора элементов SqlXml будет идти один элемент SqlResultCode, который описывает возвращаемый хранимой процедурой код.
Оба этих элемента – дочерние элементы ИмяМетодаResult, который имеет тип SqlResultStream.
Если хранимая процедура возвращает не только recordset, но еще и некоторые параметры, то после элемента ИмяМетодаResult будет следовать набор элементов, содержащих значения возвращаемых параметров. Пример:
<tns:ИмяМетодаResult xsi:type="sqlresultstream:SqlResultStream"> <sqlresultstream:SqlXml>результирующий рекордсет</sqlresultstream:SqlXml>* <sqlresultstream:SqlResultCode xsi:type="sqltypes:SqlResultCode" sqltypes:IsNested="false">возвращаемый код</sqlresultstream:SqlResultCode> </tns:ИмяМетодаResult> <tns:ИмяВыходногоПараметра>значение</tns:ИмяВыходногоПараметра>* |
По идее, SQLXML должен еще возвращать элемент SqlMessage, если в процессе выполнения запроса произошла ошибка. Я тестировал на различных конфигурациях, но никогда мне не удавалось получить от него такого сообщения. Вместо него всегда возвращался документ следующего содержания:
<?xmlversion="1.0"encoding="utf-16"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Server</faultcode> <faultstring>Runtime errors.</faultstring> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
С помощью этой опции можно настроить SQLXML так, чтобы результирующий конверт состоял из набора фрагментов в формате DiffGram, который является одним из «родных» форматов объекта DataSet из ADO.NET. Это наиболее предпочтительная опция, если клиент написан с использованием платформы .Net Framework. DiffGram-ы записываются в отдельные элементы SqlRowSet, которые являются дочерними элементу SqlResultStream. Кроме набора DiffGram, элемент SqlResultStream также содержит элемент SqlResultCode, представляющий возвращаемый код хранимой процедуры. После элемента SqlResultStream может идти неопределенное количество элементов выходных параметров. Например:
<tns:ИмяМетодаResult xsi:type="sqlresultstream:SqlResultStream"> <sqlresultstream:SqlRowSet>результирующий рекордсет в формате DiffGram</sqlresultstream:SqlRowSet>* <sqlresultstream:SqlResultCode xsi:type="sqltypes:SqlResultCode" sqltypes:IsNested="false">возвращаемый код</sqlresultstream:SqlResultCode> </tns:ИмяМетодаResult> <tns:ИмяВыходногоПараметра>значение</tns:ИмяВыходногоПараметра>* |
Эта опция с точки зрения пользователя мало чем отличается от предыдущей, если только вы не работаете с несколькими результирующими наборами. Как я уже сказал выше, в случае выбора опции DataSet Object несколько рекордсетов будут представлены соответствующим количеством фрагментов SqlRowSet. Значит, на клиенте вы будете работать с несколькими наборами отдельных объектов DataSet, что не очень удобно, учитывая, что DataSet может содержать множество таблиц. При указании параметра Single dataset SQLXML будет сохранять все рекордсеты в один DiffGram. При этом необходимость в элементе SqlRowSet отпадает. Пример:
<tns:ИмяМетодаResult xsi:type="sqlresultstream:SqlResultStream"> результирующий рекордсет в формате DiffGram <sqlresultstream:SqlResultCode xsi:type="sqltypes:SqlResultCode" sqltypes:IsNested="false">возвращаемый код</sqlresultstream:SqlResultCode> </tns:ИмяМетодаResult> <tns:ИмяВыходногоПараметра>значение</tns:ИмяВыходногоПараметра>* |
Теперь давайте, наконец, создадим несколько Web-методов для тестирования, которое будет описано в следующем разделе. Для начала нужно создать одну хранимую процедуру с output-параметром и одну функцию (ну, не нашлось в базе данных Northwind таковых). Вот скрипт их создания:
create proc test_output @i int out asset @i = 10 |
create function test_ret_func() returns int asbeginreturn 1010 end |
Теперь щелкните на кнопке с тремя точками (см. рисунок 2). Перед вами должно появится диалоговое окошко, изображенное на рисунке 3.
Рисунок 3. Хранимые процедуры и функции базы данных Northwind.
Выберите из списка: CustOrderHist, test_output и test_ret_func.
После нажатия кнопки ОК, в доселе пустом WSDL-файле, в разделе types схемы http://dcit06/srv/webserv должно появиться следующее:
<xsd:element name="CustOrderHist"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" maxOccurs="1"name="CustomerID" type="xsd:string" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="CustOrderHistResponse"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="1" maxOccurs="1"name="CustOrderHistResult" type="sqlresultstream:SqlResultStream"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="test_output"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" maxOccurs="1"name="i" type="xsd:int" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="test_outputResponse"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="1" maxOccurs="1"name="test_outputResult" type="sqlresultstream:SqlResultStream"/> <xsd:element name="i" type="xsd:int" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="test_ret_func"> <xsd:complexType> <xsd:sequence/> </xsd:complexType> </xsd:element> <xsd:element name="test_ret_funcResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="returnValue" type="xsd:int" nillable="true"/> </xsd:sequence> </xsd:complexType> </xsd:element> |
Это описания используемых типов элементов, с помощью которых происходит формирование soap-конверта.
Описания параметров выбранных методов (CustOrderHist, test_output и test_ret_func) должны появиться в соответствующих секциях message wsdl-документа.
<wsdl:message name="CustOrderHistIn"> <wsdl:part name="parameters" element="tns:CustOrderHist"/> </wsdl:message> <wsdl:message name="CustOrderHistOut"> <wsdl:part name="parameters" element="tns:CustOrderHistResponse"/> </wsdl:message> <wsdl:message name="test_outputIn"> <wsdl:part name="parameters" element="tns:test_output"/> </wsdl:message> <wsdl:message name="test_outputOut"> <wsdl:part name="parameters" element="tns:test_outputResponse"/> </wsdl:message> <wsdl:message name="test_ret_funcIn"> <wsdl:part name="parameters" element="tns:test_ret_func"/> </wsdl:message> <wsdl:message name="test_ret_funcOut"> <wsdl:part name="parameters" element="tns:test_ret_funcResponse"/> </wsdl:message> |
И, наконец, сами методы появляются в секции portType.
<wsdl:portType name="SXSPort"> <wsdl:operation name="CustOrderHist"> <wsdl:input message="tns:CustOrderHistIn"/> <wsdl:output message="tns:CustOrderHistOut"/> </wsdl:operation> <wsdl:operation name="test_output"> <wsdl:input message="tns:test_outputIn"/> <wsdl:output message="tns:test_outputOut"/> </wsdl:operation> <wsdl:operation name="test_ret_func"> <wsdl:input message="tns:test_ret_funcIn"/> <wsdl:output message="tns:test_ret_funcOut"/> </wsdl:operation> </wsdl:portType> |
Как видите, SQLXML принял решение, что все операции будут идти по сценарию запрос/ответ, о чем свидетельствуют теги input/output каждой операции.
СОВЕТ Возможно, вас не устроят названия секций, которые выбирает SQLXML. SXS – это, скорее всего, акроним от SQL Server Xml Support. Вы вправе дать всем элементами WSDL-файла свои собственные названия. Это совершенно не повлияет на работоспособность Web-сервиса. Правда, после следующей настройки с помощью консоли, все имена будут исправлены на стандартные. |
Что ж, теперь все готово к тестированию.
SOAP-сообщение – это простой XML-документ, который состоит из двух фрагментов: необязательного фрагмента Header и обязательного Body. Оба этих фрагмента должны находиться в корневом элементе Envelope. Чтобы понять, что SOAP – это довольно просто, давайте пока формировать запросы «ручками», используя для отправки пакетов компонент XMLHTTP. Все web-методы должны возвращать xml objects (см. рисунок 2).
Создавать SOAP-сообщения очень просто, если есть WSDL-документ, и вы его четко придерживаетесь. Будем двигаться небольшими шагами. Начнем с процедуры test_output. Сначала необходимо создать компонент XMLHTTP и сформировать начало SOAP-тела (Body).
Dim xmlhttp Set xmlhttp = CreateObject("Msxml2.XMLHTTP") xmlhttp.open "POST","http://dcit06/srv/webserv",falseDim soap_mes soap_mes = "<?xml version=""1.0"" encoding=""windows-1251""?>" _ & "<soap:Envelope xmlns:soap=" _ & ""http://schemas.xmlsoap.org/soap/envelope/"">" _ & "<soap:Body>" _ & ... |
Из WSDL-файла видно, что операция test_output состоит из запроса и ответа. Запрос определяется набором параметров test_outputIn (секция message под названием test_outputIn). Этот набор состоит из одного элемента test_output, в который вложен элемент i, представляющий собственно значение выходного параметра для метода., Тип элемента i - xsd:int, он может принимать значение nil. Дабы не привлекать пространство имен http://www.w3.org/2001/XMLSchema-instance, в котором определен атрибут nil, задающий пустое значение, просто передадим в качестве значения параметра i нуль.
"<test_output xmlns=""http://dcit06/srv/webserv"">" & _ "<i>0</i>" & _ "</test_output>" & _ "</soap:Body>" & _ "</soap:Envelope>" xmlhttp.send soap_mes WScript.Echo "****************************************" WScript.Echo vbTab & "Response for test_output" WScript.Echo "----------------------------------------" WScript.Echo xmlhttp.responseText |
После формирования строки запроса можно вызывать Web-сервис и вывести результаты на консоль. Подобным же образом составляется тест для хранимой процедуры CustOrderHist и функции test_ret_func.
ПРЕДУПРЕЖДЕНИЕ Мною замечена ошибка в SQLXML: параметры Web-метода трактуются как неквалифицированные элементы (unqualified elements), т.е. если написать запрос так: <t:test_output xmlns:t="http://dcit06/srv/webserv"> <t:i>0</t:i> </t:test_output> |
Наверняка через некоторое (не слишком большое) время вам надоест создавать SOAP-сообщения вручную. Раз так, переходим к следующему разделу.
ПРИМЕЧАНИЕ Прекрасным инструментом для отладки Web-сервисов является XMLSPY. Он автоматически на основе WSDL-файла создает сообщения SOAP (которые можно затем вручную отредактировать), позволяет вызвать Web-метод, а также может выступать в роли трассировщика, позволяя отлаживать Web-сервис. |
Если вы никогда не работали с SOAP Toolkit, очень рекомендую замечательную статью Ивана Андреева «Использование протокола SOAP в распределенных приложениях».
SOAP Toolkit возвращает ответ в виде объекта типа IXMLDOMNodeList. Это коллекция элементов IXMLDOMNode, у которых есть свойство xml. Его-то мы и будем использовать для вывода результатов на консоль. В коде присутствует большое количество комментариев, поэтому больше ничего говорить не буду, смотрите сами:
'Создание высокоуровнего объекта SoapClient30, реализующего динамический IDispatch 'на основе информации из WSDL-файла Dim SoapClient Set SoapClient = CreateObject("MSSOAP.SoapClient30") 'Здесь происходит чтение WSDL, анализ и генерация IDispatch,'который будет содержать все, указаные в WSDL операции'В нашем случае это test_output, test_ret_func и CustOrderHist SoapClient.MSSoapInit "http://dcit06/srv/webserv?wsdl"Dim ReturnNodeList Dim ret_val 'Вызываем Web-метод test_output. Возвращаемое значение сохраняется'в переменной ret_val'Часть необработанного SOAP-ответа возвращается в виде IXMLDOMNodeListSet ReturnNodeList = SoapClient.test_output(ret_val) 'Вывод результатов на консоль WScript.Echo "****************************************" WScript.Echo vbTab & "Response for test_output" WScript.Echo "----------------------------------------"'Перебираем элементы и выводим их на консольForEach Node In ReturnNodeList WScript.Echo Node.xml Next'Вывод на консоль возвращаемого значения WScript.Echo "Return value is " & ret_val |
Уж с чем-чем, а с поддержкой Web-сервисов в .NET Framework все в порядке. По сравнению с SOAP Toolkit, здесь все гораздо проще и легче. Чтобы начать работу, нужно создать простое консольное приложение и добавить Web-ссылку (web reference) на Web-сервис.
Примеры вызовов Web-методов тривиальны, поэтому стоит упомянуть лишь передачу ответов в формате DiffGram. Для того чтобы это стало возможным, зайдите в консоль администрирования и задайте для процедуры CustOrderHist значение Output as DataSet objects. Ниже приводится полный пример VB.NET-программы получения результатов Web-метода как объекта DataSet.
Module Module1 Sub Main() Dim prx As dcit06.procedures1 Try prx = New dcit06.procedures1 ' В момент выхове метода CustOrderHist происходит следующее' 1. Вызывается soap-метод CustOrderHist' 2. xml-фрагмент в формате DiffGram извлекается из ответа' 3. Происходит создание DataSet и вызов метода ReadXmlDim response() AsObject = prx.CustOrderHist("BLAUS") ' response(0) – готовый объект DataSet' response(1) – код возврата хранимой процедурыDim ds As DataSet = response(0) Finally prx.Dispose() EndTryEndSubEndModule |
Надо сказать, что работать с Web-сервисами из .NET Framework значительно легче, чем через Soap Toolkit. Производительность, как минимум, не хуже, а наглядность и удобство на порядок выше.
Через ADO.NET нельзя получить доступ к таким сервисным провайдерам, как SQLXMLOLEDB, поэтому комплект SQLXML 3.0 содержит сборку Microsoft.Data.SqlXml. В нее входят следующие классы:
Самый богатый свойствами и методами класс – SqlXmlCommand, с помощью которого можно выполнять запросы XPath, исполнять шаблоны и выполнять автоматическую трансформацию, причем все это может быть сделано на клиенте.
Свойства класса SqlXmlCommand
В качестве типа команды можно использовать следующие значения.
Тип | Описание |
---|---|
SqlXmlCommandType.Sql | Стандартный код T-SQL. |
SqlXmlCommandType.XPath | Запрос XPath. |
SqlXmlCommandType.Template | Выполнение шаблона. |
SqlXmlCommandType.TemplateFile | Выполнение шаблона, находящегося в файле. |
SqlXmlCommandType.UpdateGram | Выполнение апдейтаграммы. Подробности в [1] |
SqlXmlCommandType.Diffgram | Выполнение дифграммы. |
В форумах по ASP.NET иногда поднимаются вопросы наподобие: «Как наиболее эффективно и просто передать XML-документ пользователю?». Вот пример кода на VB.NET:
Dim cmd as SqlXmlCommand cmd = New SqlXmlCommand("Provider=SQLOLEDB;User ID=user;" _ & "Data Source=server;Password=password;") cmd.CommandText = "select * from [table] for xml auto" cmd.CommandType = SqlXmlCommandType.Sql cmd.RootTag = "root" Response.Clear() Response.ContentType = "text/xml" cmd.ExecuteToStream(Response.OutputStream) Response.End() |
Помимо метода ExecuteToStream, класс SqlXmlCommand предоставляет другие удобные методы выборки данных:
Примеры выполнения запросов XPath и шаблонов можно посмотреть в [1]. Более подробную информацию об использовании этих классов можно получить в [4].
Давайте создадим небольшое тестовое Intranet-приложение, которое закрепит наши знания в этой области. Я предлагаю создать простенький форум, так как он позволит нам продемонстрировать наиболее характерные особенности SQLXML.
Форум должен быть создан без единой строчки серверного кода, т.е. кода на ASP или ASP.NET. Вся серверная логика должна реализовываться с помощью хранимых процедур, которые должны быть доступны клиенту как Web-методы. Данные клиенту должны передаваться в формате XML с помощью шаблонов SQLXML. Преобразование XML в HTML будет производиться на основе XSLT-схемы, также на клиенте. Форум должен поддерживать персонализацию, т.е. возможность регистрироваться и создавать авторские сообщения. Сообщения может просматривать любой посетитель, но для создания нового сообщения необходима учетная запись. Сама страничка сообщений должна поддерживать разбивку на страницы. При этом пользователь может сам задавать размер страницы, т.е. количество сообщений на ней.
В силу того, что никакие серверные технологии не используются, поддержка сессий и приложений отсутствует.
Начнем с проектирования структуры базы данных. В приложении будут участвовать объекты: пользователи, роли пользователей и сообщения. Связь между пользователями и ролями – «многие ко многим», а между сообщениями и пользователями – «многие к одному». К атрибутам пользователя относятся: имя, полное имя, дата регистрации и пароль. Имя пользователя и пароль должны быть уникальными. Кроме этого, ни пароль, ни имя не должны допускать пустых строк. К атрибутам сообщения относятся: дата создания сообщения, тело сообщения и заголовок сообщения. Диаграмма таблиц представлена на рисунке 4.
Рисунок 4. Диаграмма таблиц БД.
Для работы с таблицами необходимы хранимые процедуры, которые будут доступны как Web-методы. Их, как и таблиц, немного:
Полный скрипт создания БД SForum, всех таблиц, хранимых процедур и пользователя (SForumUser), учетная запись которого будет использоваться для доступа к базе, можно найти среди прилагающихся к статье файлов.
Наше Web-приложение будет состоять из нескольких файлов:
Диаграмма сайта приведена на рисунке 5.
Рисунок 5. Диаграмма сайта.
Сообщения будут передаваться на страницу board.htm в формате xml, с помощью шаблона board.xml. Если вы не знакомы с шаблонами SQLXML, прочитайте соответствующий раздел первой части статьи.
<board> <info .../> <message posted="дата создания сообщения" author="автор сообщения" title="заголовок" id="идентификатор сообщения"> тело сообщения </message> </board> |
Придется учитывать тот факт, что пользователь может задавать количество сообщений на странице. Я создал дополнительный элемент info для хранения служебной информации, такой как, например, общее количество страниц при заданном размере страницы. С учетом всего вышесказанного, шаблон выглядит так:
<?xmlversion="1.0"encoding="windows-1251" ?> <board xmlns:sql="urn:schemas-microsoft-com:xml-sql"> <sql:header> <sql:param name="Page">0</sql:param> <sql:param name="PageSize">10</sql:param> </sql:header> <sql:query> exec GetMessages @Page,@PageSize </sql:query> <sql:query> select 1 as tag,0 as parent, (count(*)-1)/@PageSize + 1 as 'info!1!pages' from Messages for xml explicit </sql:query> </board> |
Страницы отсчитываются с нуля, а количество сообщений на странице по умолчанию равно десяти. Для генерации сообщений я использовал дополнительную хранимую процедуру GetMessages.
Итак, после создания БД, таблиц и хранимых процедур настала пора настроить виртуальные директории с помощью консоли «IIS Virtual Directory Management for SQLXML 3.0». Зайдите в свойства виртуальной директории srv, на вкладку Security. Необходимо поменять Credentials, под которыми SQLXML будет обращаться к базе данных. В поле User Name введите значение SForumUser, в поле Password – simple. На вкладке Data Source выберите «Use default database for current login». На вкладке Settings выберите опцию «Allow templates».
Теперь необходимо на вкладке Virtual Names настроить виртуальное имя для шаблонов. Я не буду объяснять, как это делается, вся информация содержится в [1].
Следующим шагом будет создание трех Web-методов для виртуального имени webserv: AddUser, AddMessage и IsUserRegistered. Как это сделать, подробно обсуждалось в разделе «Создание Web-методов».
Для создания самого Web-приложения откройте в VS.NET 2003 пункт «New Project», выберете тип проекта Empty Web Project (не важно для какого языка) и введите имя SForum.
Начну с создания главной странички сайта – board.htm. Я не буду приводить здесь ее целиком, выделю лишь ключевые моменты. Для скачивания файла board.xml и board.xslt используются XML-острова (XML islands).
<xml src="http://dcit06/srv/template/board.xml" id="MesSrc"></xml> <xml src="board.xslt" id="xslt"></xml> |
Сами сообщения будут находиться в теге div.
<div id="messages"/>
|
Наполнить его содержанием очень просто. Вот код, который выполняется в момент загрузки страницы:
messages.innerHTML = MesSrc.XMLDocument.transformNode(xslt.XMLDocument); |
Для обновления странички сообщений используется несколько более сложный код.
messages.innerHTML = "Подождите..."; var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); xmlhttp.open("GET", MesSrc.src + "?Page=" + curPage + "&PageSize=" _ + mes_per_page.value, false); xmlhttp.send(); MesSrc.XMLDocument = xmlhttp.responseXML; messages.innerHTML = MesSrc.XMLDocument.transformNode(xslt.XMLDocument); |
Сообщения скачиваются с помощью компонента XMLHTTP, при этом в строке запроса передаются следующие параметры: номер запрашиваемой страницы и размер страницы в сообщениях. Первый параметр хранится на страничке как переменная curPage, а второй представлен полем ввода:
<input type="text" id="mes_per_page"> |
Если все сделано правильно, страничка должна выглядеть как на рисунке 6.
Рисунок 6. Страничка board.htm.
Перейдем к следующим страницам.
Обращаться к Web-сервису из браузера мы будем с помощью «поведения Web-сервиса» (Webservice behavior). «Поведение Web-сервиса» – это отдельная очень обширная и сложная тема, которая никак не входит в рамки данной статьи. Если вы не знакомы с этой технологией или имеете смутное представление о методах ее реализации, отправляю вас к MSDN.
«Поведение Web-сервиса» позволяет вызывать Web-методы на клиенте, в коде браузера. Организовав доступ к хранимым процедурам SQL Server-а через Web-методы, можно полностью избавиться от необходимости написания какого-либо серверного кода.
Код «поведения Web-сервиса» находится в файле webservice.htc, который можно скачать по адресу http://msdn.microsoft.com/downloads/samples/internet/behaviors/library/webservice/default.asp. Связать «поведение Web-сервиса» с каким-либо элементом странички можно статически или динамически.
<body> <div id="service" style="behavior:url(webservice.htc)"></div> </body> |
После того как «поведение Web-сервиса» назначено элементу, вы должны связать его с Web-сервисом, при помощи метода useService. Обычно это делается в обработчике onload окна, однако вы вправе связать «поведение Web-сервиса» с Web-сервисом в любой удобный момент.
<script language="JavaScript"> function init() { service.useService("URL","Friendly name"); } </script> <body onload="init()"> <div id="service" style="behavior:url(webservice.htc)"></div> </body> |
С одним «поведением Web-сервиса» можно связать несколько Web-сервисов, главное чтобы их имена, передаваемые во втором параметре, несовпадали.
После этого можно вызывать Web-методы с помощью функции callService. Но прежде чем рассмотреть, как это делается, хочется остановиться вот на чем. Как вы понимаете, вызов любого Web-метода может занять продолжительное время и будет очень плохо, если приложение пользователя, в данном случае – браузер, на это время будет заблокировано. По умолчанию «поведение Web-сервиса» вызывает Web-методы асинхронно. Это значит, что результаты вызова не доступны вызывающей стороне сразу же после того, как метод callService вернул управление. «Поведение Web-сервиса» определяет набор событий, среди которых есть событие onResult, вызывающееся после получения результатов от Web-метода. Так как с одним «поведением Web-сервиса» может быть связано несколько Web-сервисов, а пользователь может одновременно вызывать несколько методов, для идентификации результатов вызова Web-метода вводится специальный счетчик. Он возвращается функцией callService и, как правило, сохраняется в глобальной переменной. В качестве счетчика в примере, приведенном ниже, используется глобальная переменная callid.
<SCRIPT language="JavaScript"> var callid = 0; function init() { service.useService("URL","MyService"); callid = service.MyService.callService("method"); } function onWSresult() { // Если в процессе вызова возникла ошибка, и это "наш" методif((event.result.error) && (callid == event.result.id)) { //event.result.errorDetail.code;//event.result.errorDetail.string;//event.result.errorDetail.raw; } // Если ошибки нет, и это "наш" методelseif((!event.result.error) && (callid == event.result.id)) { alert(event.result.value); } // Произошло что-то странноеelse { alert("Something else fired the event!"); } } </SCRIPT> <body onload="init()"> <div id="service" style="behavior:url(webservice.htc)" onresult="onWSresult()"></div> </body> |
В данном примере, функция callService принимает один параметр – имя Web-метода, однако, вы также можете указывать неопределенное количество параметров, если это требуется. Например, для Web-метода add необходимо два параметра:
callid = service.MyService.callService("add", 1, 10);
|
Как видите, в использовании «поведения Web-сервиса» нет ничего сложного.
«Поведение Web-сервиса» обладает большим количеством свойств, и рассмотреть их всех затруднительно, с их помощью вы можете указывать:
Но вернемся к нашим, как говорится, баранам. Имеется страничка Login.htm, которая содержит поля ввода для имени пользователя и пароля. Необходимо проверить, действительно ли данный пользователь зарегистрирован на сайте. Это делается с помощью Web-метода IsUserRegistered.
function onSubmit() { if (!validateForm()) { ... } else { service.Simplewebserv.callService("IsUserRegistered", NickName.value, psw.value); } } |
Функция onSubmit вызывается при нажатии на кнопку «Вход». Она проверяет корректность введенных пользователем значений и вызывает Web-метод IsUserRegistered с параметрами: имя пользователя и пароль.
function onWSresult() { if (!event.result.error){ var doc = new ActiveXObject("MSXML2.DOMDocument.3.0"); doc.loadXML(event.result.raw.xml); if (doc.parseError.errorCode == 0){ var UserIdNode = doc.selectSingleNode("//sqlresultstream:SqlResultCode"); if (UserIdNode.text == 0){ gen_error.innerText = "Неверное имя пользователя или пароль."; InvalidPsw(); InvalidNickName(); } else{ window.returnValue = "NickName:" + NickName.value + ";UserId:" + UserIdNode.text + ";"; window.close(); } } else{ gen_error.innerText = "Возникла ошибка: " + doc.parseError.reason; } } else{ gen_error.innerText = "Возникла ошибка: " + event.result.errorDetail.string; } } |
Так как SQLXML не использует кодирование RPC, нельзя воспользоваться свойством value объекта result. Вместо этого придется «вручную» проанализировать ответ Web-метода в формате xml. Удобнее всего это сделать с помощью объектной модели документа XmlDOM. Интересующее нас значение, т.е. результат вызова находится в элементе SqlResultCode (см. «XML objects»).
С помощью этой формы производится регистрация пользователя. На ней расположены поля ввода для имени пользователя, пароля и полного имени пользователя (FullName). Для регистрации вызывается метод AddUser.
service.Simplewebserv.callService("AddUser", NickName.value, psw.value,
FullName.value);
|
В общем, ничего сложного здесь нет. Результат обрабатывается тем же самым образом, что и на страничке login.htm.
Web-метод AddMessage вызывается и обрабатывается схожим образом.
Когда следует использовать описанный в статье подход? Очевидно, что для слишком больших систем он неприемлем, так как:
Этот список недостатков решения. Наоборот, для простых систем, вроде корпоративных домашних страничек, форумов и проч. такое решение соответствует как нельзя лучше.
Microsoft продолжает развивать данный подход: в новой версии SQL Server под кодовым названием «Yukon» будет встроенная поддержка Internet-сервера, а ISAPI-расширение sqlxml.dll будет входить в дистрибутив.
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |