sonyps4.ru

Создать web сервис в visual studio. Основы создания веб сервисов

Сегодня существует два фундаментально различных способа реализации Web сервисов, основанных на HTTP, в Microsoft® .NET. Первой и наиболее низкоуровневой техникой является написание специального класса IHttpHandler, который вставляется в цепочку.NET HTTP pipeline. Этот подход требует использования System.Web API для обработки входящих HTTP сообщений и System.Xml API для обработки конверта SOAP, находящегося в теле HTTP. При написании специального обработчика также требуется создать вручную документ WSDL, который точно описывает вашу реализацию. Чтобы сделать все это правильно, необходимо глубокое понимание спецификаций XML, XSD, SOAP и WSDL, что является для большинства устрашающим условием.

Более продуктивным способом реализовать Web сервисы является использование WebMethods оболочки Microsoft ASP.NET. С ASP.NET поставляется специальный класс IHttpHandler для.asmx (называемых WebServiceHandler), который обеспечивает набор необходимых вам функциональных возможностей XML, XSD, SOAP и WSDL. И, поскольку WebMethods оболочка защищает вас от сложностей, лежащих в основе XML технологий, вы можете быстро сосредоточиться на существующих проблемах бизнес логики.


Рисунок 1. Соотношение выгод и потерь между гибкостью и продуктивностью

Выбор между техниками реализации приводит к общему сравнению выгод и потерь между гибкостью и продуктивностью, как показано на Рисунке 1. Создание специального IHttpHandler дает вам неограниченную гибкость, но также требует большего времени на написание, тестирование и отладку кода. Оболочка WebMethods облегчает организацию вашего Web сервиса и быстроту разработки, но вы явно ограничены рамками оболочки. Однако в случаях, когда оболочка WebMethods не обеспечивает именно того, что вам надо, есть возможность расширить ее, добавляя собственные дополнительные функциональные возможности.

В общем, пока вы не освоили XML, XSD, SOAP и WSDL и не хотите утруждаться, работая с ними напрямую, лучше продолжайте работать с оболочкой WebMethods. Она поставляет основные сервисы, которые необходимы большинству конечных Web сервисов, а также некоторые интересные возможности расширения, которые позволяют привести оболочку в соответствие вашим конкретным надобностям. Исходя из этого, далее в статье обсуждаются внутренние механизмы работы WebMethods. Если вы новичок в XML Schema и SOAP, перед тем как продолжить прочитайте Understanding XML Schema (http://msdn.microsoft.com/webservices/building/frameworkandstudio/default.aspx?pull=/library/en-us/dnxml/html/understandxsd.asp ) и Understanding SOAP (http://msdn.microsoft.com/webservices/building/frameworkandstudio/default.aspx?pull=/library/en-us/dnsoap/html/understandsoap.asp ).
Оболочка WebMethods

Оболочка WebMethods занимается преобразованиями SOAP сообщений в методы класса.NET. Это делается, прежде всего, путем аннотирования ваших методов атрибутом , находящемся в пространстве имен System.Web.Services. Например, следующий класс.NET содержит четыре метода, два из которых аннотированы атрибутом :

using System.Web.Services; public class MathService { public double Add(double x, double y) { return x + y; } public double Subtract(double x, double y) { return x - y; } public double Multiply(double x, double y) { return x * y; } public double Divide(double x, double y) { return x / y; } }

Чтобы использовать этот класс с оболочкой WebMethods, вам надо компилировать его в сборку и скопировать в виртуальную директорию директории bin. В этом примере методы Add и Subtract затем могут быть раскрыты как операции Web сервиса, в то время как с методами Multiply и Divide этого сделать нельзя (т.к. они не были отмечены атрибутом ).

Вы раскрываете Add и Subtract как операции Web сервиса через.asmx файл. Чтобы сделать это, создайте новый текстовый файл Math.asmx, содержащий следующее простое описание, и поместите его в ту же виртуальную директорию, которая содержит и сборку (обратите внимание: файл помещается в саму виртуальную директорию, а не в ее дочернюю директорию bin):

Это описание указывает обработчику.asmx, какой класс использовать для WebMethods, а обработчик чудесным образом заботится обо всем остальном. Например, предположим, виртуальная директория называется ‘math’ и содержит Math.asmx наряду с тем, что дочерняя директория bin содержит сборку, вызов http://localhost/math/math.asmx приводит к тому, что обработчик.asmx генерирует страницу документации, показанную на Рисунке 2 (см. далее).

Это один из основных вариантов работы обработчика.asmx. Файл.asmx обычно содержит только описание WebService, которое ссылается на класс Web сервиса по имени (как в примере, показанном ниже). Следовательно, в этом случае, сборка должна уже быть скомпилирована и размещена в директории bin виртуальной директории. Обработчик.asmx также обеспечивает JIT компиляцию исходного кода, находящегося в файле.asmx. Например, следующий файл (названный Mathjit.asmx) содержит описание WebService вместе с исходным кодом класса, на который ссылается.

using System.Web.Services; public class MathServiceJit { public double Add(double x, double y) { return x + y; } public double Subtract(double x, double y) { return x - y; } public double Multiply(double x, double y) { return x * y; } public double Divide(double x, double y) { return x / y; } }

Когда к этому файлу доступаются через HTTP впервые, обработчик.asmx компилирует источник и правильно размещает сборку. Обратите внимание, что в описании WebService должен быть также указан и язык программирования, чтобы обработчик.asmx мог выбрать в среде выполнения правильный компилятор. Явным недостатком этого подхода является то, что вы не знаете об ошибках компиляции до тех пор, пока не обратитесь к файлу.


Рисунок 2. Документация MathService

При создании нового проекта Web сервиса в Visual Studio® .NET вы всегда используете технику “двух файлов”, когда исходный файл класса отделен от файла.asmx, который на него ссылается. Интегрированная среда разработки (IDE) хорошо скрывает это от вас, но если вы нажмете Show All Files на панели инструментов Solution Explorer, вы увидите в проекте по два файла для каждого класса Web сервиса. Кстати, Visual Studio .NET не поддерживает для файлов.asmx синтаксическое выделение или IntelliSense®, поэтому вы сами решаете, следовать ли в этом направлении. В Web проектах Visual Studio .NET также заботится о создании виртуальной директории и автоматическом компилировании сборки в директорию bin виртуальной директории.

Перед погружением в детали работы обработчика.asmx давайте коротко обсудим, как обрабатываются сообщения от Internet Information Server (IIS), прежде всего, в обработчике.asmx. Когда входящее HTTP сообщение достигает порта 80, для того, чтобы определить, какая ISAPI DLL должна использоваться для его обработки, IIS использует информацию метабазы IIS. Инсталляция.NET связывает расширения.asmx с Aspnet_isapi.dll, как показано на Рисунке 3.



Рисунок 3. Связываение IIS и.asmx

Aspnet_isapi.dll – это стандартное расширение ISAPI, поставляемое.NET Framework, которое просто направляет HTTP запросы в отдельный рабочий процесс, называемый Aspnet_wp.exe. Aspnet_wp.exe выполняет роль хоста для общеязыковой среды выполнения и.NET HTTP pipeline. Как только сообщение входит в.NET HTTP pipeline, pipeline просматривает в файле конфигурации, какой класс IHttpHandler надо использовать для данного расширения. Если вы посмотрите свой файл Machine.config, то увидите, что он содержит httpHandler, связанный с расширением.asmx, как показано здесь:

...

Итак, когда сообщение поступает в.NET HTTP pipeline, нацеливаясь на файл.asmx, pipeline заходит в класс WebServiceHandlerFactory, чтобы создать новый объект WebServiceHandler, который может использоваться для обработки запроса (с помощью метода IHttpHandlerProcessRequest). Затем объект WebServiceHandler открывает физический файл.asmx, чтобы определить имя класса, содержащего ваш WebMethods. Более подробная информация о работе.NET HTTP pipeline представлена в HTTP Pipelines: Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET (http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/02/09/HTTPPipelines/default.aspx).

Сразу после того, как.NET HTTP pipeline вызывает обработчик.asmx, чудесным образом начинается обработка XML, XSD, SOAP и WSDL. Остальные функциональные возможности, предоставляемые обработчиком.asmx, могут быть разделены на три основные группы: 1) координирование сообщений, 2) преобразование XML в объекты и 3) автоматическое генерирование WSDL и документации. Давайте каждую из этих групп рассмотрим более детально.
Диспечеризация сообщений

Когда HTTP pipeline вызывает обработчик.asmx, просматривая описание WebService в файле.asmx, он определяет, какой класс.NET использовать. Затем он просматривает поступившее HTTP сообщение, чтобы определить, какой именно метод вызывать в данном классе. Чтобы вызвать операцию Add, показанную в предыдущих примерах, входящее HTTP сообщение должно выглядеть примерно так:

POST /math/math.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://tempuri.org/Add" 33 66

На самом деле во входящем HTTP сообщении есть два участка, которые могут использоваться для определения метода, который должен быть вызван в классе: заголовок SOAPAction или имя запрашиваемого элемента (т.е. имя элемента в пределах элемента soap:Body). В этом случае хотя бы один из них определяет имя метода, который хочет вызвать отправитель.

Для диспечеризации сообщения обработчик.asmx по умолчанию использует заголовок SOAPAction. Значит, обработчик.asmx смотрит на заголовок SOAPAction в сообщении, а затем, используя.NET рефлексию, проверяет методы класса. Он рассматривает только методы, помеченные атрибутом , но, просматривая значение SOAPAction каждого метода, он точно определяет, какой метод вызывать. Поскольку мы явно не определили значение SOAPAction в методах нашего класса, обработчик.asmx принимает, что значением SOAPAction будет сочетание пространства имен Web сервиса и имени метода. Поскольку мы также не определили и пространство имен, обработчик по умолчанию присваивает http://tempuri.org. Таким образом, значение по умолчанию SOAPAction для метода Add будет http://tempuri.org/Add.

Вы можете изменить пространство имен Web сервиса, помечая класс атрибутом , так же как и точное значение SOAPAction, помечая WebMethods атрибутом , как показано далее:

using System.Web.Services; using System.Web.Services.Protocols; public class MathService { public double Add(double x, double y) { return x + y; } public double Subtract(double x, double y) { return x - y; } ... }

Теперь обработчик.asmx ожидает, что значение SOAPAction для метода Add будет http://example.org/math/Add и urn:math:subtract для метода Subtract (поскольку мы явно задали это значение). Например, следующее сообщение HTTP запроса вызывает операцию Subtract:

POST /math/math.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "urn:math:subtract" 33 66

Если обработчик.asmx не находит SOAPAction, подходящего для входящего HTTP сообщения, он просто формирует исключительную ситуацию (позже рассмотрим, как обрабатываются исключительные ситуации). Если для диспечерезации метода вы не используете заголовок SOAPAction, помечая класс свойством RoutingStyle атрибута , вы можете указать обработчику.asmx использовать имя элемента запроса. Если вы делаете это, то также надо указать, что WebMethods не нуждаются в значении SOAPAction, путем установления их значений в пустую строку, как показано ниже:

using System.Web.Services; using System.Web.Services.Protocols; public class MathService { public double Add(double x, double y) { return x + y; } public double Subtract(double x, double y) { return x - y; } ... }

В этом случае обработчик даже не рассматривает значение SOAPAction – он использует вместо него имя элемента запроса. Например, ожидается,что имя элемента запроса для метода Add будет Add (из пространства имен http://example.org/math), как показано в этом сообщении HTTP запроса:

POST /math/math.asmx HTTP/1.1 Host: localhost Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "" 33 66

Значит, первое, что делает обработчик.asmx при получении входящего HTTP сообщения, – он определяет, как перенаправить сообщение в соответствующий WebMethod. Однако до того как он действительно сможет вызвать метод, он должен преобразовать входящий XML в.NET объекты.
Преобразование XML в объекты

Как только обработчик WebMehod определил, какой метод надо вызвать, он должен десериализовать XML сообщение в.NET объекты, которые могут быть предоставлены во время вызова метода. Так же как и при диспечеризации сообщения, обработчик проверяет класс с помощью рефлексии, чтобы выяснить, как обрабатывать входящее XML сообщение. Класс XmlSerializer осуществляет автоматическое преобразование между XML и объектами в пространстве имен System.Xml.Serialization.

XmlSerializer делает возможным преобразование любого public типа.NET в тип XML Schema и, вместе с тем, может проводить автоматические преобразования между.NET объектами и документами XML (см. Рисунок 4). XmlSerializer ограничен теми возможностями, которые сегодня поддерживает XML Schema, поэтому он не может работать со всеми сложностями сегодняшних объектных моделей, такими как комплексные не древовидные диаграммы объектов, дублирующие указатели и т.д. Несмотря на это, XmlSerializer может работать с большинством комплексных типов, используемых разработчиками.

Для приведенного выше примера Add XmlSerializer преобразует элементы x и y в.NET double значение, которые затем смогут предоставляться при вызове Add. Метод Add возвращает вызывающему значение типа double, которое затем должно быть опять сериализовано в XML элемент в рамках SOAP ответа.



Рисунок 4. Преобразование XML в объекты

Также XmlSerializer может автоматически работать с комплексными типами (за исключением описанных выше ограничений). Например, следующий WebMethod вычисляет расстояние между двумя структурами Point:

using System; using System.Web.Services; public class Point { public double x; public double y; } public class Geometry { public double Distance(Point orig, Point dest) { return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) + Math.Pow(orig.y-dest.y, 2)); } }

SOAP сообщение запроса для этой операции будет содержать элемент Distance, который включает два дочерних элемента, orig и dest, и каждый из этих элементов должен содержать дочерние x и y элементы:

0 0 3 4

В этом случае SOAP сообщение ответа будет содержать элемент DistanceResponse, включающий элемент DistanceResult типа double:

5

Применяемое по умолчанию XML преобразование использует имя метода в качестве имени элемента запроса и имена параметров в качестве имен дочерних элементов. Структура каждого параметра зависит от структуры типа. Имена public полей и свойств просто преобразовываются в дочерние элементы, как в случае x и y (в Point). Именем элемента ответа по умолчанию становится имя элемента запроса, оканчивающееся словом "Response". Элемент ответа также содержит дочерние элементы, названные так же как и элементы запроса, только оканчивающиеся словом "Result".

Есть возможность освободиться от стандартного XML преобразования путем использования большого количества встроенных атрибутов преобразования. Например, для преобразования имени типа и пространства имен можно использовать атрибут . Чтобы контролировать то, как параметры или члены класса преобразовывают элементы или атрибуты, вы можете пользоваться атрибутами и , соответственно. А для контроля за тем, как сам метод преобразовывается в имена элементов в сообщениях запроса/ответа, можно воспользоваться атрибутом . Например, ознакомьтесь со следующей версией Distance, в которой используются различные атрибуты:

using System; using System.Web.Services; using System.Web.Services.Protocols; using System.Xml.Serialization; public class Point { public double x; public double y; } public class Geometry { public double Distance(Point orig, Point dest) { return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) + Math.Pow(orig.y-dest.y, 2)); } }

В этой версии Distance ожидается, что входящее SOAP сообщение будет выглядеть следующим образом:

И будет сгенерировано такое SOAP сообщение ответа:

5

Для реализации и определения приведенного выше преобразования, применяемого по умолчанию, обработчик.asmx использует документ/литерал стиль SOAP. Это означает, что описание WSDL будет содержать буквенные определения XML schema, описывающие и элементы запроса, и элементы ответа, используемые в SOAP сообщениях (т.е. правила SOAP кодирования не используются).

Обработчик.asmx также делает возможным использование rpc/литерал стиля SOAP. Это означает, что SOAP Body содержит XML представление вызова RPC и параметры сериализовываются с использованием правил SOAP кодирования (т.е. XML Schema не нужна). Соответственно, вместо атрибутов и вы используете и . Более подробно о различиях в этих стилях см. в разделе MSDN Understanding SOAP (http://msdn.microsoft.com/webservices/building/frameworkandstudio/default.aspx?pull=/library/en-us/dnsoap/html/understandsoap.asp).

Как видите, есть возможность полностью изменить процесс преобразования данного метода в SOAP сообщение. XmlSerializer обеспечивает мощный механизм сериализации со многими возможностями, о которых у нас нет времени здесь говорить. Более подробно о том, как работает XmlSerializer, см. в разделе MSDN Moving to .NET and Web Services (http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/01/11/webserv/). Я также рассмотрел многие нюансы XmlSerializer, не раскрытые в моей ежемесячной колонке MSDN Magazine, XML Files (http://msdn.microsoft.com/msdnmag/find/default.aspx?type=Ti&phrase=XML%20Files) (смотрите список колонок в online архивах).

Кроме работы по десериализации параметров, обработчик.asmx также может десериализовать/сериализовать SOAP заголовки. SOAP заголовки обрабатываются не так, как параметры, поскольку они обычно считаются несвязанной информацией без прямой привязки к конкретному методу. Поэтому обработка заголовка обычно делается с помощью уровней перехвата, полностью защищающих WebMethods от необходимости иметь дело с обработкой заголовков.

Если, все-таки, вы хотите использовать информацию заголовка из WebMethod, вы должны предоставить класс.NET, наследуемый от SoapHeader, который представляет XML Schema заголовков (следуя рекомендациям преобразования, приведенным выше). Затем вы определяете переменную члена этого типа, выполняющую роль «заполнителя» для экземпляров заголовка. И наконец, вы помечаете каждый WebMethod, нуждающийся в доступе к заголовку, определяя имя того поля, куда вы хотите его направить.

Например, SOAP запрос, содержащий заголовок UsernameToken с целью аутентификации:

Mary yraM ...

Чтобы сделать возможной для обработчика.asmx десериализацию заголовка, сначала надо определить класс.NET, представляющий предполагаемый тип XML Schema (обратите внимание: если у вас на самом деле есть XML Schema для заголовка, вы можете генерировать класс, используя xsd.exe /c). В этом случае соответствующий класс выглядит следующим образом:

Public class UsernameToken: SoapHeader { public string username; public string password; }

Затем просто надо определить переменную члена в вашем классе WebMethod, чтобы хранить экземпляр класса заголовка, и аннотировать WebMethods атрибутом , как показано ниже:

using System; using System.Web.Services; using System.Web.Services.Protocols; public class Geometry { public UsernameToken Token; public double Distance(Point orig, Point dest) { if (!Token.username.Equals(Reverse(Token.password))) throw new Exception("access denied"); return Math.Sqrt(Math.Pow(orig.x-dest.x, 2) + Math.Pow(orig.y-dest.y, 2)); } }

Затем в WebMethod вы можете обратиться к полю Token и извлечь информацию, которая была предоставлена заголовком. Вы также можете отправить заголовки обратно клиенту, используя ту же технику – вам просто надо определить направление заголовка в описании атрибута . Более подробную информацию об обработке SOAP заголовков в оболочке WebMethods см. в разделе MSDN Digging into SOAP Headers with the .NET Framework. (http://msdn.microsoft.com/webservices/building/frameworkandstudio/default.aspx?pull=/library/en-us/dnservice/html/service06182002.asp)

Обработчик.asmx также обеспечивает автоматическую сериализацию исключительных ситуаций.NET. Любая необработанная исключительная ситуация,перехваченная обработчиком.asmx, автоматически сериализуется в элемент SOAP Fault в ответе. Например, в предыдущем примере, если имя пользователя не совпадает с паролем, наш код формирует исключительную ситуацию.NET. Тогда обработчик.asmx обработает ее и сериализует в SOAP ответ, как показано ниже:

soap:Server Server was unable to process request. --> access denied

Если вы хотите еще больше контролировать элемент SOAP Fault, вы можете также явно сформировать объект SoapException, определяя все детали элемента SOAP Fault, такие как элементы faultcode, faulstring, faultactor и detail. Более подробная информации представлена в разделе MSDN Using SOAP Faults (http://msdn.microsoft.com/webservices/building/frameworkandstudio/default.aspx?pull=/library/en-us/dnservice/html/service09172002.asp).

Как видите, чтобы понять, как работает WebMethods, необходимо еще разобраться с механизмом, лежащим в основе сериализации, и всем многообразием его возможностей. Преимуществом механизма сериализации является то, что он скрывает весь лежащий в основе XML API код, который обычно вам приходится писать в специальном обработчике. Однако, в то время как большинство разработчиков считают это положительным моментом, некоторые называют это недостатком, потому что все еще хотят самостоятельно работать с SOAP сообщением в пределах реализации WebMethod. Более детально о том, как использовать такой смешанный подход, см. в разделе MSDN Accessing Raw SOAP Messages in ASP.NET Web Services (http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/03/03/WebServices/default.aspx).
Автоматическое генерирование WSDL

Клиентам надо точно знать, как должно выглядеть SOAP сообщение, чтобы успешно работать с ним. Общепринятым является предоставлять описания Web сервиса через WSDL (и встроенные XSD определения). Для этого обработчик.asmx автоматически генерирует и страницу документации, и описание WSDL, которое точно отражает интерфейс WebMethod. Если вы применили группу атрибутов преобразования к вашим WebMethods, все они будут отражены в сгенерированной документации.

Если вы посмотрите файл.asmx, то найдете страницу документации, такую как показано на Рисунке 2. Эта страница документации генерируется страницей.aspx, называемой DefaultWsdlHelpGenerator.aspx (находящейся в C:windowsMicrosoft.NETFramework v1.0.3705config). Если откроете файл, вы увидите, что это всего лишь стандартная страница ASP.NET, которая использует.NET рефлексию для генерирования документации. Эта возможность позволяет документации всегда соответствовать коду. Просто модифицируя этот файл, вы можете изменять генерируемую документацию.

Также можно блокировать генерирование документации на основе виртуальной директории, определяя другой файл документации в файле Web.config:

...

Если клиент завершает запрос GET для.asmx символами "?wsdl" в строке запроса, обработчик.asmx вместо документации генерирует описание WSDL. Клиенты могут использовать описание WSDL для генерирования proxy классов, которые автоматически знают, как общаться с Web сервисом (т.е. используя Wsdl.exe в.NET).

Чтобы изменить процесс генерирования WSDL, вы можете написать класс SoapExtensionReflector и зарегистрировать его с оболочкой WebMethods в файле Web.config. Затем, когда обработчик.asmx будет генерировать описание WSDL, он вызовет ваш класс и даст вам возможность изменить окончательное описание, поставляемое клиенту. Более подробно о том, как писать классы SoapExtensionReflector, см. в разделе MSDN SoapExtensionReflectors in ASP.NET Web Services (http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20030320WebServicesMP/manifest.xml).

Используя две различные техники, вы можете полностью блокировать процесс генерирования WSDL. Во-первых, для доступа клиентами вы можете предоставить статический WSDL документ в вашей виртуальной директории и затем блокировать генератор документации, удалив его из вашего файла Web.config, как показано ниже:

...

Немного более автоматизированной техникой является использование атрибута для определения местоположения статического документа WSDL в виртуальной директории, которую реализовывает класс WebMethod. Вы также должны определить имя WSDL binding, которое реализовывает каждый WebMethod, используя атрибут . Процесс автоматического генерирования WSDL импортирует ваш статический WSDL файл и «завернет» его в новое описание сервиса. Более подробная информация по этой технике представлена в статье MSDN Place XML Message Design Ahead of Schema Planning to Improve Web Service Interoperability (http://msdn.microsoft.com/library/default.asp?url=/msdnmag/issues/02/12/WebServicesDesign/).

Сегодня очень тяжело вручную создавать WSDL, потому что до сих пор нет достаточного количества доступных редакторов WSDL. Поэтому автоматическое генерирование документации/ WSDL является ценной частью оболочки WebMethods, которая облегчит жизнь многим разработчикам.
Заключение

Оболочка WebMethods ASP.NET обеспечивает высокопроизводительный подход к построению Web сервисов. WebMethods сделали возможным использовать традиционные методы.NET в качестве операций Web сервиса, которые поддерживают HTTP, XML, XML Schema, SOAP и WSDL. Обработчик WebMethod (.asmx) автоматически определяет, как отправить входящие SOAP сообщения в соответствующий метод, в какой точке он автоматически сериализует входящие XML элементы в соответствующие объекты.NET. И упрощая жизнь клиента, обработчик.asmx также обеспечивает автоматическую поддержку для генерирования документации для человека (HTML) и для машины (WSDL).

Хотя по сравнению со специальным IHttpHandlers оболочка WebMethods может выглядеть несколько ограниченной, она обеспечивает мощную модель расширяемости, известную как оболочка SOAP расширения. Расширения SOAP обеспечивают возможность для удовлетворения ваших конкретных нужд ввести дополнительные функциональные возможности, помимо обсуждаемых здесь. Например, Microsoft выпустила Web Services Enhancements 1.0 для Microsoft .NET (WSE), которые просто предоставляют класс SoapExtension, вводящий в оболочку WebMethods поддержку для нескольких GXA спецификаций. Более подробная информация по написанию SOAP расширений представлена в MSDN Fun with SOAP Extensions (http://msdn.microsoft.com/webservices/building/frameworkandstudio/default.aspx?pull=/library/en-us/dnaspnet/html/asp03222001.asp).

HttpModule - глобальный обработчик запросов ко всем страницам приложения, с помощью которого можно "вклиниваться" в различные этапы обработки запроса (не…

Тип double - это основной тип данных, который используется для переменных, содержащих числа с дробной частью. Double используется в C, C++, C# и других языках…

Однажды посмотрев HTML-документ, генерируемый ASP.NET и содержащий GridView, я заметил, что большое количество символов, содержащихся на странице - пробелы и знаки…

В нынешние времена бурного развития интренета все чаще и чаще возникает проблема связи между данными, расположенными в разных концах земного шара на различных платформах и в разнообразных источниках данных. И эта проблема все больше увеличивается с ростом компаний, использующих интернет и веб приложения в своем бизнесе. Microsoft .NET веб сервисы решают проблему такой связи.

Не вдаваясь в глубины теории веб сервисов и вопросы о том, как и почему они работают, данная статья целиком и полностью посвящена вопросу создания веб сервисов. В течении нескольких последующих страниц будут рассмотрены многие аспекты создания веб сервисов и написано небольшое приложение, использующее технологию веб сервисов.

Итак, как же выглядит веб сервис для разработчика? Первое – файл web-сервиса имеет расширение asmx. Второе – создание web-сервиса немногим отличается от создания веб-формы в.NET Framework. Третье – файл веб сервиса должен начинаться с директивы WebService. Четвертое – класс web-сервиса может (но не должен) быть потомком класса System.Web.Services.Webservice. Ну и последнее (пока что) – метод, вызываемый через веб, должен имет атрибут WebMethod.

Основываясь на вышесказанном начнем реализовывать веб сервис (все примеры в этой главе основаны на codebehind и сделаны с помощью Visual Studio.NET).

Создадим новое приложение в VS.NET и добавим к нему файл веб сервиса nw.asmx (ну или откроем notepad и создадим 2 файла – nw.asmx и nw.asmx.cs).

Файл nw.asmx содержит единственную строку – директиву WebService, которая утверждает, что этот файл – действительно веб сервис. Этот файл меняться не будет, так что можете взглянуть на него и пока забыть:).

Весь код веб сервиса будет располагаться в codebehind файле nw.asmx.cs. Изначально этот файл (созданный в Visual Studion.NET) имеет следующий вид:

Using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace WebServicesExample { /// /// Summary description for nw. /// public class nw: System.Web.Services.WebService { public nw() { //CODEGEN: This call is required by the ASP.NET Web Services Designer InitializeComponent(); } #region Component Designer generated code //Required by the Web Services Designer private IContainer components = null; /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { } /// /// Clean up any resources being used. /// protected override void Dispose(bool disposing) { if(disposing && components != null) { components.Dispose(); } base.Dispose(disposing); } #endregion // WEB SERVICE EXAMPLE // The HelloWorld() example service returns the string Hello World // To build, uncomment the following lines then save and build the project // To test this web service, press F5 // // public string HelloWorld() // { // return "Hello World"; // } } }

Как видите ребята из Microsoft сразу же позаботились о начинающих программистах и лишили работы многие поколения учителей, жутко любивших начинать свои рассказы с крика «Hello, World!»:). Теперь вы сами не прилагая никаких усилий можете создать этот код просто откомментировав объявление метода HelloWorld() :).

Ну что ж, сделаем это (зря, что ли ребята старались? :) и запустим, предварительно откомпилировав проект, на выполнение наш веб сервис (откроем в браузере страницу http://localhost/WebServicesExample/nw.asmx). В браузере нарисуется вот такая страница:


ASP.NET для отображения web-сервиса использует файл шаблона DefaultWsdlHelpGenerator.aspx, расположенный в папке %SYSTEM_ROOT%\Microsoft.NET\Framework\\CONFIG. На выводимой странице веб сервиса есть название веб сервиса (отмечено 1), ссылка на описание сервиса (2) (эта ссылка в дальнейшем будет интересовать нас при создании клиентов к веб сервису) и список веб методов, объявленных в веб сервисе (3). Остальная часть страницы посвящена тому, как плохо использовать неймспейс по умолчанию для веб сервиса, и рекомендациям срочно исправить это упущение:). Нет проблем и пока это свежо у нас в памяти поступим как рекомендуют – добавим атрибут WebService с параметром Namespace к классу веб сервиса:

Итак, перейдем к странице описания web-метода SayHello (просто кликните по ссылке SayHello на странице описания web-сервиса).


Как видите на ней также присутствует название веб сервиса, ссылка на первую страницу веб сервиса, название веб метода. Кроме этого на странице расположена форма, предназначеная для вызова web метода через GET запрос (данное правило не выполняется, если web метод не может быть вызван таким образом) и примеры запросов для вызова данного веб метода с помощью SOAP, HTTP POST (если такой вызов возможен) и HTTP GET (если такой вызов возможен). Также представлены примеры ответов вызова web метода.

При желании вы можете сейчас протестировать метод нажав на кнопку Invoke (у меня уже такого желания нет:)). Лучше перейдем к созданию более полезных веб методов.

Я долго думал какой же веб метод предложить первым для изучения. Что-то простое, возвращающее единственное значение, или помонструозней да с DataSetами? И решил все таки в первом же примере показать как можно больше возможностей веб сервисов.

Итак, задача – торговая фирма, имеющая БД со всеми необходимыми ей для работы данными (ну как тут без Northwind обойтись:)), открывает филиал в другом районе/городе/конце земного шара. При этом возникает естесственная потребность работникам филиала обеспечить доступ к БД фирмы. Но вокруг одни ограничения – база централизована и копию создавать нельзя, а в филиале вообще всего пара компьютеров, которые только и могут, что выйти в интернет. Но работать им как-то с базой все таки надо... :). Хотя бы иметь возможность получать заказы по указанным клиентам.

Задача поставлена, теперь необходимо ее реализовывать. Для начала мы создадим веб сервис, возвращающий DataSet с заказами указанного клиента, а потом напишем 2 клиента для этого веб сервиса, один на DHTML для IE, второй на ASP.NET (через некоторое время филиал все таки разбогател и купил себе компьютер для веб сервера:)).

Для получения интересующих нас данных мы будем использовать несколько измененную хранимую процедуру CustOrdersOrders:

CREATE PROCEDURE CustOrdersOrdersDetails @CustomerID nchar(5) AS SELECT OrderID, OrderDate, RequiredDate, ShippedDate FROM Orders WHERE CustomerID = @CustomerID ORDER BY OrderID SELECT Products.ProductName, .UnitPrice, .Quantity, .Discount, .OrderID FROM INNER JOIN Products ON .ProductID = Products.ProductID where OrderID in (SELECT OrderID FROM Orders WHERE CustomerID = @CustomerID)

Веб метод, возвращающий данные по выбранному клиенту, имеет следующий вид:

Public DataSet GetCustOrders(string CustomerID) { SqlConnection myConn = new SqlConnection("server=localhost;database=Northwind;uid=sa;pwd=Manowar"); SqlDataAdapter myData = new SqlDataAdapter("CustOrdersOrdersDetails", myConn); myData.SelectCommand.CommandType = CommandType.StoredProcedure; myData.SelectCommand.Parameters.Add(new SqlParameter("@CustomerID", SqlDbType.Char, 5)); myData.SelectCommand.Parameters["@CustomerID"].Value = CustomerID; DataSet ds = new DataSet(); myData.Fill(ds); ds.Tables.TableName = "Orders"; ds.Tables.TableName = "OrderDetails"; ds.Relations.Add(ds.Tables.Columns["OrderID"], ds.Tables.Columns["OrderID"]); return ds; }

Ничего экстраординарного – DataSet заполняется данными, полученными вызовом созданной нами хранимой процедуры, к нему добавляется отношение между таблицами и затем результат возвращается. Ну и естесственный атрибут WebMethod для метода говорит о том, что мы сможем вызвать этот метод из веб сервиса.

Протестируем нашу работу с помощью страницы веб сервиса. Откройте в браузере страницы веб сервиса (у меня это http://localhost/WebServicesExample/nw.asmx), нажмите на GetCustOrders для перехода с странице веб метода, введите в поле ввода ID клиента из базы Northwind (например ALFKI) и нажмите кнопку Invoke. Откроется новая страница, содержащая в себе XML. Это и есть результат вызова нашего веб метода – необходимый нам DataSet со списком заказов клиента ALFKI. Ниже представлены фрагменты страницы с результатами.

… 10643 1997-08-25T00:00:00.0000000+03:00 1997-09-22T00:00:00.0000000+03:00 1997-09-02T00:00:00.0000000+03:00 10692 1997-10-03T00:00:00.0000000+03:00 1997-10-31T00:00:00.0000000+02:00 1997-10-13T00:00:00.0000000+03:00 … Rössle Sauerkraut 45.6 15 0.25 10643 Chartreuse verte 18 21 0.25 10643 …

Рассмотрим повнимательней что же нам верноул веб метод.

Из приведенного выше кода можно сделать следующие выводы:

  • Возвращаемый результат имеет тип DataSet (это видно как из названия корневого узла, так и из строки в описании схемы).
  • Возвращаемый DataSet содержит 2 таблицы – Orders и OrderDetails.
  • В схеме доступна полная онформация о таблицах, а также запись о созданном нами отношении между таблицами.

Конечно же, если бы нам для вызова web-метода приходилось заходить на страницу его описания, вручную задавать параметры вызова web-метода и вызывать его, толку от этого было бы мало. Как я уже говорил, web-методы можно вызывать с помощью HTTP GET, HTTP POST и SOAP запросов.

Сейчас мы рассмотрим возможности работы с созданным web-сервисом с помощью HTTP GET (или HTTP POST) из браузера Internet Explorer (тот самый случай, когда работники филиала нашей гипотетической фирмы не имеют ничего кроме компьютера с выходом в интернет), а также SOAP вызов с помощью небольшой программы, написанной на C#.

Как же это делается? Воспользуемся объектом Microsoft.XMLHTTP, позволяющим передавать и получать данные с помощью запросов GET или POST. Мы создаем запрос к соответствующему web-методу нашего web-сервиса, передаем (если необходимо) параметры и выводим на страницу полученный в ответ XML код, используя небольшой xsl-файл.

Ниже представлен код orders.xsl (предназначенного для отображения полученного результата) и DhtmlClient.htm (основного файла).

Order:
Date:
Ship:
Product:Price:Quantity:Discount:
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); var objStyle = new ActiveXObject("MSXML.DOMDocument"); objStyle.async = false; objStyle.load("orders.xsl"); var SOAPRequest = new ActiveXObject("MSXML.DOMDocument"); SOAPRequest.async = false; var SOAPResponse = new ActiveXObject("MSXML.DOMDocument"); SOAPResponse.async = false; function getOrders() { // xmlhttp.Open("GET", "http://localhost/WebServicesExample/nw.asmx/GetCustOrders?CustomerID=" + // document.all.client.value, false); xmlhttp.Open("POST", "http://localhost/WebServicesExample/nw.asmx/GetCustOrders", false); xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlhttp.send("CustomerID=" + document.all.client.value); SOAPResponse.loadXML(xmlhttp.responseXML.xml); document.all.orders.innerHTML = SOAPResponse.transformNode(objStyle.documentElement); }
Клиент

Откройте файл DhtmlClient.html в броузере, введите код клиента в поле ввода (ALFKI подойдет просто замечательно) и нажмите кнопку «Заказы». JavaScript метод getOrders() направит запрос через HTTP POST к веб методу и затем преобразует возвращенный результат с помощью таблицы стилей orders.xsl и выведет его на страницу (если вы хотите протестировать вызов веб метода через HTTP GET – откомментируйте первую строку метода и закомментируйте три последующих). При этом у вас получится страница, похожая на вот это:


Теперь рассмотрим вызов web-метода из.NET-приложения. Для вызова web-сервисов.NET-приложение использует прокси-класс, генерируемый либо с помощью утилиты командной строки wsdl, либо с помощью добавления Web Reference в проект Visual Studio .NET. Прокси-класс, генерируемый таким образом, содержит довольно много кода. Но для нас представляет ценность то, что в нем содержатся методы для вызова интересующих нас web-методов.

Напишем небольшое Windows Forms приложение, вызывающее метод GetCustOrders нашего веб сервиса. В Visual Studio.NET создадим Windows приложение. Добавим в созданное приложение ссылку на web-сервис (меню Project – Add Web Reference…). В адресную строку открывшегося окна введем адрес нашего web-сервиса (http://localhost/WebServicesExample/nw.asmx):


После нажатия на кнопку Add Reference Visual Studio.NET добавит в проект ссылку на web-сервис и автоматически сгенерирует прокси-класс для вызова web-методов.

Тем, кто не пользуется Visual Studio.NET, создание прокси класса также не представляет особого труда. Для этого, как я уже говорил, используется утилита командной строки wsdl. Полное описание использования этой утилиты можно найти в MSDN, здесь же я только покажу вызов этой утилиты для генерации прокси класса для нашего веб сервиса.

Wsdl /l:cs http://localhost/WebServicesExample/nw.asmx

При этом будет создан файл nw.cs, содержащий небходимый нам прокси класс.

Продолжим создание Windows клиента для форума. Разместим на форме поле ввода для ввода кода клиента, DataGrid для отображения полученных данных и, естесственно, кнопку «Заказы». Для того, чтобы в DataGridе отобразились интересующие нас данные, в обработчик OnClick кнопки необходимо вставить всего 2 строки:

Private void btnOrders_Click(object sender, System.EventArgs e) { localhost.nw orders = new localhost.nw(); dgMain.DataSource = orders.GetCustOrders(txtClient.Text); }

Запустите созданное приложение, введите ALFKI в поле ввода и нажмите кнопку «Заказы». В DataGridе отобразится полученный в результате вызова DataSet с интересующими нас данными:


Как видите создание и использование web-сервисов в.NET не такая уж и сложная задача.

Теперь рассмотрим строение web-сервиса, что называется, шаг за шагом.

В первой (и единственной, если вы пишете с использованием технологии codebehind) строке asmx файла, как и в случае с aspx-файлом, расположена директива ASP.NET, указывающая на тип данного файла, язык, на котором написан код, и имя класса для файла. Например строка для написанного нами web-сервиса следующая:

где директива WebService указывает на то, что данный файл является web-сервисом, а атрибуты Language=”c#” и Class=”WebServicesExample.nw” указывают на то, что класс web-сервиса написан на C# и полное имя класса – WebServicesExample.nw.

Web-сервис может состоять из множества классов. Однако только один класс в web-сервисе может иметь методы, помеченные атрибутом WebMethod (которые можно вызывать через SOAP-запросы).

Атрибут WebMethod имеет шесть свойств, влияющих на работу web-метода. Рассмотрим их:

Description

Данное свойство служит для общего описания web-метода. Вы можете присвоить данному свойству любую текстовую строку. Значение свойства Description выводится на странице описания web-сервиса. Возвращаясь к рассмотренному ранее примеру опишем web-метод getAuthors следующим образом:

Public DataSet GetCustOrders(string CustomerID)

Откомпилируем наш проект и взглянем теперь на страницу описания web-сервиса:


Как видите, теперь под методом GetCustOrders выведено его описание.

EnableSession

Данное свойство позволяет включить поддержку сессий. По умолчанию поддержка сессий в web-сервисах отключена. Чтобы включить ее, определите web-метод следующим образом:

Если при объявлении web-сервиса вы породили его от класса System.Web.Services.WebService, то вы автоматически получаете доступ к открытым свойствам Application, Context, Session, Server и User, имеющим такой же смысл, как и аналогичные свойства ASP.NET веб форм. Если же вы создавали класс web-сервиса как-то иначе – ничего страшного. Вы все равно можете получить доступ к вышеперечисленным свойствам с помощью соответствующих свойств статического HttpContext.Current.

Рассмотрим работу с объектами Application и Session на примере. Напишем небольшой web-сервис с двумя web-методами – setSessionVar (принимает 2 строковых параметра – имя переменной сессии и ее значение, и устанавливает переменную сессии) и getSessionVar (принимает строковый параметр – имя сессии, и возвращает значение переменной сессии):

Public void setSessionVar(string name, string val) { Session = val; } public string getSessionVar(string name) { return (string) Session; }

Не забывайте, что ASP.NET определяет сессию по идентификатору сессии, хранящемуся в cookie. Так что приведенный выше пример будет успешно работать из страницы web-сервиса, но если попробовать вызывать методы этого web-сервиса из.NET приложения через прокси-класс, то вы с удивлением обнаружите, что вызов метода getSessionVar не возвращает никакого значения, так как использование web-сервисов с помощью проки-класса по умолчанию не использует куки для сохранения информации.

Чтобы.NET приложение могло работать с сессией с помощью web-сервиса, необходимо добавить в код, вызывающий методы web-сервиса, работу с cookie. И это не так уж сложно, как кажется на первый взгляд:).

Создадим Windows приложение, добавим в него Web Reference на созданный ранее web-сервис и добавим интерфейс для установки переменных сессии с помощью метода setSessionVar и получения значения переменной сессии с помощью getSessionVar. Объявим также в класс приватную переменную cookie типа CookieContainer (класс CookieContainer описан в модуле System.Net). В конструкторе класса проиницализируем эту переменную. Теперь для того, чтобы вызовы методов web-сервиса были в одной сессии, необходимо просто перед вызовом web-метода присвоить значение cookie свойству CookieContainer класса web-сервиса.

Полностью код вызова веб методов с использованием cookie представлен ниже (прокси-класс, сгенерированный для web-сервиса, имеет название localhost1.testService1):

Private CookieContainer cookie; public Form1() { InitializeComponent(); cookie = new CookieContainer(); } private void btnSetValue_Click(object sender, System.EventArgs e) { localhost.session session = new localhost.session(); session.CookieContainer = cookie; session.setSessionVar(txtSessionName.Text, txtSessionValue.Text); } private void btnGetValue_Click(object sender, System.EventArgs e) { localhost.session session = new localhost.session(); session.CookieContainer = cookie; txtSessionValue.Text = session.getSessionVar(txtSessionName.Text); }

MessageName

Свойство MessageName позволяет назначать web-методу имя, отличное от того, которое ему было назначено при написании класса web-сервиса.

Существует возможность создавать в web-сервисе web-методы с одинаковыми именами. Но при попытке просмотра страницы такого web-сервиса будет сгенерирована ошибка. Свойство MessageName используется именно в этих случаях.

Допустим кроме веб метода GetCustOrders с одним параметром (код клиента) у нас объявлен еще один метод GetCustOrders, принимающий кроме кода клиента также вилку дат и возвращающий DataSet с информацией о заказах, дата которых находится между указанными датами

Public DataSet GetCustOrders(string CustomerID) { ... } public DataSet GetCustOrders(string CustomerID, DateTime startDate, DateTime endDate) { ... }

Теперь, хотя оба web-метода и имеют одинаковое имя, все равно есть возможность использовать страницу web-сервиса. При этом в прокси классе оба метода естесственно будут определены с именем GetCustOrders.

TransactionOption

Web-сервисы ограниченно поддерживают транзакции. С помощью свойства TransactionOption можно управлять тем, как ваш метод использует транзакции. Это свойство может принимать следующие значения:

Слово «ограниченно» означает, что веб сервис может порождать транзакцию, но при этом не может быть участником другой транзакции. Если вызывается веб метод с TransactionOption установленным в Required или RequiresNew, а в нем вызывается другой веб метод с такими же установками, каждый из этих методов инициирует свою транзакцию.

По умолчанию свойство TransactionOption установлено в Required.

При нормальном завершении работы метода транзакция считается выполненной. Чтобы прервать выполнение транзакции, сгенерируйте исключение. В веб методе не нужно вызывать методы SetComplete и SetAbort.

CacheDuration

Сказать, что кеширование веб сервисов просто означало бы покривить душой. Кешировать веб сервисы не просто, а очень просто. Одним словом, все, что нужно для того, чтобы активизировать кеширование веб сервиса, это использовать параметр CacheDuration атрибута WebMethod с указанием промежутка времени в секундах, на которое кешируется веб сервис.

Ниже представлен пример обхъявления, кеширующий возвращаемое методом GetCustOrders значение на 10 минут.

Public DataSet GetCustOrders(string CustomerID)

BufferResponse

Свойство BufferResponse позволяет управлять буферизацией ответа web-метода. По умолчанию результат выполнения буферизируется и отправляется клиенту только после того, как он полностью сформирован. Однако если ваш web-метод очень долго выполняется, возможно, имеет смысл отключать буферизацию результата.

Для отключения буферизации результата используйте следующее объявление web-метода:

Public DataSet GetCustOrders(string CustomerID)

Параметры атрибута WebService

Web-сервис также имеет три свойства, влияющие на его работу. Два из них, Description и Name, работают точно так же, как и подобные им свойства Description и MessageName web-метода. Третье свойство, Namespace, позволяет управлять пространством имен в SOAP-сообщении. Рассмотрим применение всех трех свойств на примере web-сервиса wsauthors.

Изменим объявление web-сервиса на следующее:

Public class nw: System.Web.Services.WebService

после чего откомпилируем web-сервис и посмотрим на страницу описания:


На страницу было добавлено описание web-сервиса, введенное в свойстве Description, а также изменилось имя web-сервиса. Обратите внимание на тот факт, что теперь обращаться к web-сервису нужно по новому имени (вам необходимо будет заново сгенерировать прокси-класс для web-сервиса, и этот прокси-класс будет иметь новое имя).

И напоследок рассмотрим вопрос типов данных, которые может принимать в качестве параметров и возвращать веб метод.

Веб методы могут принимать как параметры и возвращать результатом любые значимые типы данных, массивы этих типов и структуры и классы, состоящие из этих типов. Если говорить проще, то web-сервис умеет работать с любым типом данных, который может быть представлен в виде XDR схемы.

Рассмотрим веб метод GetCustomer, возвращающий структуру Customer, содержащую информацию о клиенте. Для начала определим класс Customer

Public class Customer { private string id; private string companyName; private string address; public Customer() { } public Customer (string id, string companyName, string address) { this.id = id; this.companyName = companyName; this.address = address; } public string ID { get { return id; } set { id = value; } } public string CompanyName { get { return companyName; } set { companyName = value; } } public string Address { get { return address; } set { address = value; } } }

В принципе ничего сложного – приватные переменные, публичные свойства простого типа и пара конструкторов. Не удивляйтесь наличию конструктора без параметров, хоть он и ничего реально не делает, но наличие публичного конструктора по умолчанию – требование сериализируемым классам. Вообщем наш класс полностью удовлетворяет требованиям, налагаемым на типы для использования их в веб сервисах.

Теперь напишем веб метод, возвращающий переменную данного типа.

Public Customer GetCustomer(string CustomerID) { SqlConnection myConn = new SqlConnection("server=localhost;database=Northwind;uid=sa;pwd=Manowar"); SqlCommand myCmd = new SqlCommand("select CustomerID, CompanyName, Address from Customers " + "where CustomerID = "" + CustomerID + """, myConn); myConn.Open(); SqlDataReader reader = myCmd.ExecuteReader(); Customer customer; if(reader.Read()) customer = new Customer(reader.GetString(0), reader.GetString(1), reader.GetString(2)); else customer = new Customer(); reader.Close(); myConn.Close(); return customer; }

В результате вызова этого веб метода для многострадального клиента ALFKI будет возвращен следующий XML.

ALFKI Alfreds Futterkiste Obere Str. 57

Еще один вопрос, связанный с возвратом данных из web-сервиса, касается вида XML-кода, возвращаемого web-методом. Как видно из предыдущго примера, web-метод возвращает данные в виде XML-элементов с названиями, соответствующими тем названиям, которые были описаны в описании класса. Вы можете изменить как названия XML-элементов, так и возвращать некоторые (или все) данные в виде XML-атрибутов.

Изменим немного описание структуры Customer для иллюстрации сказанного. Теперь поле CustomerID, будет возвращаться как атрибут ID, а поле CompanyName поменяет свое название на Company.

Public class Customer { ... public string ID ... public string CompanyName ... }

Теперь в результате выполнения предыдущего web-метода через веб интерфейс будет получен следующий XML:

Alfreds Futterkiste Obere Str. 57

Заключение

Веб сервисы представляют собой новый вид веб приложений для создания уровня бизнес логики и связи разнородных приложений с помощью использования общих стандартов – HTTP, XML и SOAP. Практически не существует ограничений на области применения веб сервисов: вы можете создавать веб сервисы для взаимодействия между различными приложениями или для предоставления данных вашим клиентам. Возможности использования веб сервисов теоретически ограничены только вашим воображением.

Текст примеров данной статьи можно выкачать

A web service is a web-based functionality accessed using the protocols of the web to be used by the web application s. There are three aspects of web service development:

  • Creating the web service
  • Creating a proxy
  • Consuming the web service
Creating a Web Service

A web service is a web application which is basically a class consisting of methods that could be used by other applications. It also follows a code-behind architecture such as the ASP.NET web pages, although it does not have a user interface.

To understand the concept let us create a web service to provide stock price information. The clients can query about the name and price of a stock based on the stock symbol. To keep this example simple, the values are hardcoded in a two-dimensional array. This web service has three methods:

  • A default HelloWorld method
  • A GetName Method
  • A GetPrice Method

Take the following steps to create the web service:

Step (1) : Select File -> New -> Web Site in Visual Studio, and then select ASP.NET Web Service.

Step (2) : A web service file called Service.asmx and its code behind file, Service.cs is created in the App_Code directory of the project.

Step (3) : Change the names of the files to StockService.asmx and StockService.cs.

Step (4) : The .asmx file has simply a WebService directive on it:

Step (5) : Open the StockService.cs file, the code generated in it is the basic Hello World service. The default web service code behind file looks like the following:

Using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Linq; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using System.Xml.Linq; namespace StockService { // // Summary description for Service1 // // To allow this Web Service to be called from script, // using ASP.NET AJAX, uncomment the following line. // public class Service1: System.Web.Services.WebService { public string HelloWorld() { return "Hello World"; } } }

Step (6) : Change the code behind file to add the two dimensional array of strings for stock symbol, name and price and two web methods for getting the stock information.

Using System; using System.Linq; using System.Web; using System.Web.Services; using System.Web.Services.Protocols; using System.Xml.Linq; // To allow this Web Service to be called from script, // using ASP.NET AJAX, uncomment the following line. // public class StockService: System.Web.Services.WebService { public StockService () { //Uncomment the following if using designed components //InitializeComponent(); } string[,] stocks = { {"RELIND", "Reliance Industries", "1060.15"}, {"ICICI", "ICICI Bank", "911.55"}, {"JSW", "JSW Steel", "1201.25"}, {"WIPRO", "Wipro Limited", "1194.65"}, {"SATYAM", "Satyam Computers", "91.10"} }; public string HelloWorld() { return "Hello World"; } public double GetPrice(string symbol) { //it takes the symbol as parameter and returns price for (int i = 0; i < stocks.GetLength(0); i++) { if (String.Compare(symbol, stocks, true) == 0) return Convert.ToDouble(stocks); } return 0; } public string GetName(string symbol) { // It takes the symbol as parameter and // returns name of the stock for (int i = 0; i < stocks.GetLength(0); i++) { if (String.Compare(symbol, stocks, true) == 0) return stocks; } return "Stock Not Found"; } }

Step (7) : Running the web service application gives a web service test page, which allows testing the service methods.

Step (8) : Click on a method name, and check whether it runs properly.


Step (9) : For testing the GetName method, provide one of the stock symbols, which are hard coded, it returns the name of the stock


Consuming the Web Service

For using the web service, create a web site under the same solution. This could be done by right clicking on the Solution name in the Solution Explorer. The web page calling the web service should have a label control to display the returned results and two button controls one for post back and another for calling the service.

The content file for the web application is as follows:

Untitled Page Using the Stock Service



The code behind file for the web application is as follows:

Using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; //this is the proxy using localhost; namespace wsclient { public partial class _Default: System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { lblmessage.Text = "First Loading Time: " + DateTime.Now.ToLongTimeString } else { lblmessage.Text = "PostBack at: " + DateTime.Now.ToLongTimeString(); } } protected void btnservice_Click(object sender, EventArgs e) { StockService proxy = new StockService(); lblmessage.Text = String.Format("Current SATYAM Price:{0}", proxy.GetPrice("SATYAM").ToString()); } } }

Creating the Proxy

A proxy is a stand-in for the web service codes. Before using the web service, a proxy must be created. The proxy is registered with the client application. Then the client application makes the calls to the web service as it were using a local method.

The proxy takes the calls, wraps it in proper format and sends it as a SOAP request to the server. SOAP stands for Simple Object Access Protocol. This protocol is used for exchanging web service data.

When the server returns the SOAP package to the client, the proxy decodes everything and presents it to the client application.

Before calling the web service using the btnservice_Click, a web reference should be added to the application. This creates a proxy class transparently, which is used by the btnservice_Click event.

Protected void btnservice_Click(object sender, EventArgs e) { StockService proxy = new StockService(); lblmessage.Text = String.Format("Current SATYAM Price: {0}", proxy.GetPrice("SATYAM").ToString()); }

Take the following steps for creating the proxy:

Step (1) : Right click on the web application entry in the Solution Explorer and click on "Add Web Reference".


Step (2) : Select "Web Services in this solution". It returns the StockService reference.


Step (3) : Clicking on the service opens the test web page. By default the proxy created is called "localhost", you can rename it. Click on "Add Reference" to add the proxy to the client application.


Include the proxy in the code behind file by adding.

Эти ограничения не препятствуют использованию функции обратного вызова ASP.NET AJAX по ее прямому назначению - в качестве механизма выполнения страницей задач серверного приложения. Если ранее вам приходилось использовать веб-службы для представления серверной функциональности "толстым" клиентам, сторонним разработчикам и приложениям на платформах, отличных от.NET, вы увидите, что применение веб-служб в ASP.NET AJAX несколько проще.

Существует ряд способов обхода этих ограничений. Например, в своем веб-приложении можно вызвать веб-метод, который, в свою очередь, обращается к веб-методу, существующему в другом домене. Этот метод установления связи работает, поскольку код веб-сервера не имеет ограничения, присущего браузеру - он волен запускать междоменные вызовы других веб-служб.

Создание веб-службы

Веб-службы, используемые с AJAX ASP.NET, состоят из двух частей: файла.asmx, который действует в качестве конечной точки веб-службы, и файла.cs, который содержит фактический код C#. Эти файлы необходимо добавить к веб-сайту, содержащему страницу AJAX ASP.NET, которая будет использовать веб-службу.

Самый быстрый способ создания веб-службы в среде Visual Studio - выбрать пункт меню Website --> Add New Item, указать шаблон Web Service, назначить имя файлу (в следующем примере - TerritoriesService) и щелкнуть на кнопке Add (Добавить). При создании веб-сайта без проекта файл.asmx будет помещен в каталог веб-приложения, а соответствующий файл.cs - в папку App_Code для автоматической компиляции.

Чтобы веб-службы можно было использовать с ASP.NET AJAX, веб-приложение не обязательно размещать в виртуальном каталоге IIS. Вместо этого для тестирования приложения можно применять встроенный веб-сервер Visual Studio. Это возможно потому, что код сценария, который автоматически вызывает веб-службу, использует относительный путь. В результате, независимо от порта, выбираемого веб-сервером Visual Studio, веб-страница сможет сформировать правильный URL-адрес.

Файл.asmx не представляет особого интереса - если его открыть, в нем обнаружится единственная строка с директивой WebService, которая определяет язык кода, расположение файла отделенного кода и имя класса:

В этом примере создается веб-служба TerritoriesService.asmx с файлом отделенного кода TerritoriesService.cs. В файле отделенного кода определен класс TerritoriesService, который выглядит следующим образом:

Public class TerritoriesService: System.Web.Services.WebService { // ... }

По умолчанию атрибут ScriptService закомментирован. Чтобы создать веб-службу, которую можно будет вызывать из страницы ASP.NET AJAX, не забудьте удалить символы комментария.

Этот класс является производным от System.Web.Services.WebService , который служит традиционным базовым классом для веб-служб. Однако этот подход выбран лишь для удобства и не является обязательным. Наследование от WebService предоставляет доступ к ряду встроенных объектов (таких как Application, Server, Session и User) без необходимости обращения к статическому свойству HttpContext.Current.

Обратите также внимание, что объявление класса веб-службы содержит три атрибута. Два первых - WebService (устанавливает пространство имен XML, используемое в сообщениях веб-службы) и WebServiceBinding (указывает уровень соответствия стандартам, поддерживаемый веб-службой) - применяются только при вызове веб-службы с помощью сообщений SOAP и не имеют значения в страницах ASP.NET AJAX. Однако третий атрибут - ScriptService - значительно важнее. Он конфигурирует веб-службу, разрешая JSON-вызовы из клиентов JavaScript. Без этого веб-службу нельзя было бы применять в странице ASP.NET AJAX.

Создание веб-метода

После выполнения описанных действий можно приступать к написанию кода для своей веб-службы. Каждая веб-служба содержит один или более методов, которые помечены атрибутом WebMethod . Этот атрибут делает метод доступным для удаленного вызова. Если добавить метод, который не содержит атрибут веб-метода, серверный код сможет его использовать, но клиентский код JavaScript не сможет вызывать его непосредственно.

Public class TerritoriesService: System.Web.Services.WebService { public string HelloWorld() { return "Hello World"; } }

He обязательно делать метод общедоступным (как в этом примере), но обычно это делают согласно принятым соглашениям. На веб-методы накладываются определенные ограничения. Типы данных, используемые для значений параметров и возвращаемых значений, должны быть из числа тех, что описаны в таблице ниже:

Допустимые типы данных параметров и возвращаемых значений веб-службы Тип данных Описание
Базовые типы

Базовые типы данных C#, такие как целые числа (short, int, long), целые без знака (ushort, uint, ulong), нецелочисленные числовые типы (float, double, decimal) и ряд других смешанных типов (bool, string, char, byte, DateTime)

Перечисления

Типы перечислений (определенные в C# с помощью ключевого слова enum) поддерживаются. Однако, веб-служба использует строковое имя значения перечисления (а не лежащее в основе целое число)

Специальные объекты

Можно передать любой объект, созданный на основе специального класса или структуры. Единственное ограничение - то, что передаются только общедоступные члены данных и свойства, и все общедоступные члены и свойства должны относиться к одному из поддерживаемых типов данных. Если использовать класс, который включает специальные методы, эти методы не будут переданы клиенту и не будут ему доступны

Массивы и коллекции

Можно использовать массивы любого поддерживаемого типа. Допускается также ArrayList (который просто преобразуется в массив), но более специализированные коллекции, такие как Hashtable, не разрешены. Можно применять базовые коллекции. Во всех этих случаях объекты в коллекции должны быть также сериализуемыми

XmlNode

Объекты, основанные на System.Xml.XmlNode, являются представлениями части XML-документа. Их можно применять для отправки произвольного XML-текста

DataSet и DataTable

DataSet и DataTable можно использовать для возврата информации из реляционной базы данных. Другие объекты данных ADO.NET, такие как DataColumns и DataRows, не поддерживаются. Применяемый объект DataSet или DataTable автоматически преобразуется в XML-фрагмент, подобно тому, как это происходит при использовании методов GetXml() или WriteXml()

Состояние сеанса в веб-службе

Атрибут WebMethod принимает ряд параметров, большинство из которых несет определенную нагрузку в странице ASP.NET. Одним исключением является свойство EnableSession , которое по умолчанию имеет значение false, в результате чего состояние сеанса визуализации веб-службе недоступно. Это значение по умолчанию имеет смысл в традиционной веб-службе, не использующей ASP.NET AJAX, поскольку какая-либо информация о сеансе может не существовать, а клиент может вообще не поддерживать cookie-набор сеанса. Но в случае веб-службы ASP.NET AJAX вызовы веб-службы всегда осуществляются из контекста веб-страницы ASP.NET, которая выполняется в контексте текущего пользователя веб-приложения, и у этого пользователя имеются действующий сеанс и cookie-набор сеанса, автоматически передаваемые вместе с вызовом веб-службы.

Ниже приведен пример предоставления веб-методу доступа к объекту Session:

Public void DoSomething() { if (Session["myObject"] != null) { // (Использовать объект в состоянии сеанса.) } else { // (Создать новый объект и сохранить его в состоянии сеанса.) } }

Для примера с раскрывающимся списком веб-служба должна обеспечить способ получения регионов, расположенных на данной территории. Ниже приведен код веб-службы, содержащей веб-метод GetTerritoriesInRegion(), который извлекает регионы:

Using System; using System.Collections.Generic; using System.Web; using System.Web.Services; using System.Data; using System.Data.SqlClient; using System.Web.Configuration; public class TerritoriesService: System.Web.Services.WebService { public List GetTerritoriesInRegion(int regionID) { SqlConnection con = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString); SqlCommand cmd = new SqlCommand("SELECT * FROM Territories WHERE RegionID=@RegionID", con); cmd.Parameters.Add(new SqlParameter("@RegionID", SqlDbType.Int, 4)); cmd.Parameters["@RegionID"].Value = regionID; List territories = new List(); try { con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { territories.Add(new Territory(reader["TerritoryID"].ToString(), reader["TerritoryDescription"].ToString())); } reader.Close(); } catch { // Маскировать ошибки throw new ApplicationException("Ошибка данных"); } finally { con.Close(); } return territories; } }

Код метода GetTerritoriesInRegion() подобен коду, который применялся ранее для обслуживания клиентского обратного вызова. Однако у этого кода имеется основное отличие - вместо того, чтобы возвращать единственную длинную строку с результатами, он возвращает информацию, используя строго типизированный список объектов Territory. Это - намного более строгий подход, предотвращающий случайные ошибки.

Класс Territory охватывает две части строковой информации. В нем определены общедоступные переменные-члены, а не свойства, поскольку он призван играть роль исключительно контейнера данных, который транспортирует информацию по сети:

Public class Territory { public string ID; public string Description; public Territory(string id, string description) { this.ID = id; this.Description = description; } public Territory() { } }

Это определение класса можно поместить в тот же файл кода, что и веб-служба, или в отдельный файл внутри каталога App_Code.

Вызов веб-службы

Теперь, когда нужная веб-служба создана, необходимо сконфигурировать страницу так, чтобы ей было известно о службе TerritoriesService. Для этого к странице нужно добавить элемент управления ScriptManager. Затем в дескриптор этого элемента управления потребуется добавить раздел .

С помощью элементов ServiceReference этого раздела перечисляются все используемые страницей службы и их расположения. Добавление ссылки на ранее приведенный файл TerritoriesService .asmx выполняется следующим образом:

При визуализации страницы на сервере ScriptManager будет генерировать прокси-объект JavaScript. В клиентском коде этот прокси-объект JavaScript можно применять для выполнения вызовов. Ниже приведен код двух списков, помещенных в веб-форму:

Первый список заполняется посредством обыкновенной привязки данных ASP.NET с помощью элемента управления источником данных SqlDataSource. Больший интерес представляет то, что он использует атрибут onchange для привязки к клиентскому обработчику события. В результате, когда пользователь выбирает новую территорию, JavaScript-функция GetTerritories() запускается, и текущее значение списка передается в качестве аргумента.

Формально весь код функции GetTerritories() можно было бы поместить непосредственно в атрибут события onchange, тем самым уменьшив количество создаваемых функций JavaScript. Однако отделение кода, который вызывает веб-службу, улучшает читабельность кода и облегчает его сопровождение.

Код JavaScript-функции GetTerritories() имеет следующий вид:

function GetTerritories(regionID) { TerritoriesService.GetTerritoriesInRegion(regionID, OnRequestComplete, OnError); }

Если вам ранее приходилось программировать с применением веб-служб ASP.NET, вы заметите, что синтаксис клиентского кода вызова веб-службы ASP.NET AJAX отличается от синтаксиса.NET. В приложении.NET сначала должен быть создан прокси-объект, а затем вызвана веб-служба на этом объекте. В странице ASP.NET AJAX используется готовый прокси-объект с тем же именем, что и у класса веб-службы.

Клиентские вызовы веб-службы являются асинхронными, поэтому наряду с исходными параметрами веб-метода всегда нужно предоставлять один дополнительный параметр, идентифицирующий клиентскую функцию JavaScript, которая должна вызываться после получения результата. (В рассматриваемом примере это функция OnRequestComplete.) Дополнительно можно добавить еще одну ссылку, указывающую на функцию, которая должна использоваться при возникновении ошибки. (В данном примере это функция OnError.)

Чтобы завершить пример, нужно предоставить клиентскую функцию, которая обрабатывает ответ. В данном примере это функция OnRequestComplete(). Она принимает возвращаемое значение в единственном параметре, а затем добавляет информацию во второй раскрывающийся список на веб-странице:

Function OnRequestComplete(result) { var lstTerritories = document.getElementById("lstTerritories"); lstTerritories.innerHTML = ""; for (var n = 0; n

Примечательной особенностью этого кода является то, что он в состоянии работать с результатом, возвращенным из веб-метода, без выполнения каких-либо дополнительных действий по десериализации. Еще больше впечатляет то, что веб-метод возвращает обобщенный список объектов Territory, который, очевидно, не имеет никакого эквивалента в коде JavaScript. Вместо этого ASP.NET AJAX создает определение для объекта Territory и возвращает полный список в массиве. Это позволяет коду JavaScript просматривать в цикле массив и проверять свойства ID и Description каждого элемента.

В данном случае можно воспользоваться одним небольшим ухищрением. Вместо метода document.getElementById() допускается применять псевдоним $get из ASP.NET AJAX, который выполняет ту же функцию и имеет следующий вид:

Var lstTerritories = $get("lstTerritories");

Этот прием - обычное соглашение, применяемое в страницах ASP.NET AJAX.

Теперь этот пример работает совершенно так же, как версия клиентского обратного вызова, описанная ранее. Различие между ними состоит в том, что эта версия использует строго типизированный веб-метод без громоздкого кода сериализации строк. Кроме того, не нужно добавлять никакой серверный код для динамического приема ссылки обратного вызова и ее вставки. Вместо этого можно применять простой прокси-объект, который обеспечивает доступ к веб-службе.

В качестве последнего штриха можно задать лимит времени и функции обработки ошибок, например:

Function OnError(result) { var lbl = document.getElementById("lblInfo"); lbl.innerHTML = "" + result.get_message() + "
"; lbl.innerHTML += result.get_stackTrace(); }

Функция OnError() получает объект ошибки, содержащий метод get_message() , который извлекает текст ошибки, и метод get_stackTrace() , который возвращает подробный стек вызовов с указанием места возникновения ошибки. На рисунке ниже показано, что происходит, когда веб-методу не удается подключиться к базе данных, и он генерирует стандартное исключение ApplicationException:

В этом примере продемонстрирована версия ASP.NET AJAX модели клиентского обратного вызова. Хотя она использует те же внутренние механизмы, что и функция клиентского обратного вызова ASP.NET, версия ASP.NET AJAX обеспечивает более прочный фундамент построенный на основе веб-служб. Тем не менее, оба подхода обладают одной общей чертой - независимо от применяемого подхода придется писать собственный код JavaScript для обновления страницы.

Помещение веб-метода в страницу

В большинстве случаев для обработки обратных вызовов ASP.NET AJAX целесообразно создавать отдельную веб-службу. Обычно этот подход ведет к созданию более четких страниц и облегчает отладку и совершенствование кода. Тем не менее, в некоторых ситуациях могут существовать один или более веб-методов, которые явно предназначены для использования на единственной странице и действительно не будут применяться в других частях приложения. В этом случае можно создать выделенную веб-службу для каждой страницы или же переместить код веб-службы в страницу.

Помещение кода веб-метода в страницу не представляет сложности - фактически, для этого достаточно простого перетаскивания. Для начала скопируйте свой веб-метод (дополненный атрибутом WebMethod) в класс отделенного кода страницы. Затем измените его на статический метод и добавьте атрибут System.Web.Script.Services.ScriptMethod . Ниже приведен пример помещения веб-метода (GetTerritoriesInRegion) в веб-страницу:

Public partial class _Default: System.Web.UI.Page { public static List GetTerritoriesInRegion(int regionID) { // Передать работу классу веб-службы TerritoriesService service = new TerritoriesService(); return service.GetTerritoriesInRegion(regionID); } }

Установите свойство ScriptManager.EnablePageMethods в true и удалите ссылку в разделе кода ScriptManager (при условии, что не собираетесь использовать какие-либо веб-службы, не встроенные в страницу):

И, наконец, измените код JavaScript так, чтобы он вызывал метод посредством объекта PageMethods, как показано в следующем примере:

Function GetTerritories(regionID) { PageMethods.GetTerritoriesInRegion(regionID, OnRequestComplete, OnError); }

Объект PageMethods представляет все веб-методы, добавленные в текущую веб-страницу.

Одно из преимуществ помещения веб-метода в страницу состоит в том. что метод больше не представляется через файл.asmx. В результате он не считается частью общедоступной веб-службы, и его обнаружение кем-либо посторонним затруднено. Это обстоятельство - веский аргумент, если веб-службы желательно скрыть от любопытных глаз.

Еще одна возможная причина кодирования веб-методов непосредственно в классе страницы - необходимость считывания значений из состояния представления или элементов управления, расположенных на странице. При вызове метода страницы запускается упрощенная версия жизненного цикла страницы, как это имело место при использовании функции клиентского обратного вызова ASP.NET. Конечно, попытка модификации деталей страницы лишена какого-либо смысла, поскольку страница не визуализируется и, следовательно, любые вносимые изменения будут просто отброшены.

С точки зрения безопасности приложения не имеет никакого значения, размещаются веб-методы в странице или в выделенной веб-службе. Размещение веб-метода в странице может скрыть его от случайных пользователей, но настоящий взломщик начнет с просмотра HTML-кода страницы, который включает в себя ссылку на прокси-объект JavaScript. Злоумышленники могут легко использовать прокси-объект JavaScript для выполнения подложных вызовов веб-метода. Для защиты от подобных угроз веб-методы должны всегда реализовывать те же самые меры безопасности, которые применяются в веб-страницах. Например, любой принимаемый ввод должен быть проверен на достоверность, код должен отказываться возвращать уязвимую информацию не аутентифицированным пользователям, а при доступе к базе данных должны применяться параметризованные команды для предотвращения атак внедрением SQL.



Загрузка...