Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » .NET - Web Services »

Обсудить на форуме Обсудить на форуме

Создание совместимых Web-сервисов с использованием VS.NET
В статье описывает пример создания Web-сервиса на VB.NET, а также его использования из PHP, Perl, VB, ASP, C#.

Введение

Активно распространяющаяся сейчас технология Web Services дает большие преимущества при разработке различных распределенных приложений. Можно легко интегрировать приложения, разработанные на разных платформах, не заботясь об ограничениях локальной сети и наличии FireWall. Ведь используются только принятые стандарты и открытые протоколы. Все хорошо, очень здорово и очень удобно. Но как всегда, есть одно маленькое «но». Каждое средство разработки, поддерживающее Web Services делает это немного по-своему. И, если не знать, эти мелочи, то процесс разработки может занять достаточно много времени.

В этой статье хотелось бы поделиться опытом, полученном специалистами компании Metric, при разработке Web-сервисов для компании Аэрофлот.

Описание задачи

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

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

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

Создаем Web-сервис на .NET

Общую процедуру создания сервиса мы описывать не будем, она описана в документации, а также в других статьях в разделе XML Web Services на GotDotNet.RU. Остановимся только на моментах, связанные с совместимостью:
  • Используйте атрибут SoapRpcService
  • Возвращайте массивы объектов, а не массивы структур.
  • Избегайте использования типа dateTime в аргументах.

Используйте SoapRpcService

VS.NET поддерживает два стиля для кодирования данных при передаче SOAP-сообщений: RPC и Document. Если Вы не указываете дополнительных атрибутов, то по умолчанию VS.NET использует стиль Document. Однако многие программные продукты других производителей поддерживают только стиль RPC. Поэтому, чтобы Ваш сервис был доступен наиболее широкому кругу клиентов, необходимо использовать именно его. Для этого необходимо использовать класс System.Web.Services.Protocols и указать атрибут SoapRpcService.
[VB.NET]
Imports System.Web.Services
Imports System.Web.Services.Protocols
<SoapRpcService(), WebService(Namespace:="http://www.aeroflot.ru/")> _
Public Class FlightStatus
	Inherits System.Web.Services.WebService
	...
End Class

[C#]
using System.Web.Services;
using System.Web.Services.Protocols;
namespace Aeroflot
{
	[WebService(Namespace="http://www.aeroflot.ru/")]
	[SoapRpcService]
	public class FlightStatus : System.Web.Services.WebService
	{
	....
	}
}
Аналогично для методов необходимо указывать атрибут SoapRpcMethod.

Возвращайте массив объектов, а не массив структур.

При создании сервиса, использующего Document стиль кодирования, Вы можете объявить структуру и возвращать массив структур.
Public Structure t
	Public a As String
	Public b As String
End Structure


 Public Function Test() As t()
	Dim t1() As t

	Test = t1
End Function
Однако, как только Вы укажете инструкцию SoapRpcService, получите сообщение, что массивы структур не поддерживаются при таком стиле кодирования сообщений (Arrays of structs are not supported with encoded SOAP.).

Поэтому, Вам необходимо объявить публичный класс и возвращать массив элементов этого класса.

 

Избегайте использование типа dateTime в аргументах.

При написании клиентов пришлось столкнуться с тем, что попытки передачи дат из разных языков часто приводили к ошибке. WebService не желал воспринимать дату в передаваемом формате. Поэтому, чтобы упростить процедуру взаимодействия с сервисом, лучше такие параметры объявлять строковыми, а приведение выполнять уже внутри функции. Так с сервисом будет просто проще работать.

Если объединить все эти правила, то код сервиса на VB.NET будет выглядеть примерно так:

Imports System.Web.Services
Imports System.Web.Services.Protocols

<SoapRpcService(), WebService(Namespace:="http://www.aeroflot.ru/")> _
Public Class FlightStatus
	Inherits System.Web.Services.WebService

	Public Class Airport
		Public city As String
		Public name As String
		Public code As String
	End Class


	<WebMethod(), SoapRpcMethod()> _
	Public Function AirportList () As Airport()
		Dim OutArray() As Airport

		…………........  Заполняем массив данными ……………..
		AirportList = OutArray
	End Function

End Class

Пишем клиентов

Сервис создан и продемонстрируем, как к нему обращаться из разных языков программирования.

В качестве примера будет демонстрироваться вызов функции получения табло прилета по аэропорту Шереметьево 1 (код SVO1) за текущий день. После получения результата будем печатать следующие поля:

  • Компания
  • Рейс
  • Аэропорт
  • Дата по расписанию
  • Ожидаемая дата
Полный код примеров и описание сервиса есть на сервере http://webservices.aeroflot.ru/.

Клиент на C#

Для начала напишем программу-клиент на одном из .NET языков – С#.
  • Создаем новый проект.
  • Через Add Web Reference добавляем ссылку на автоматически создаваемый .disco-файл ( http://webservices.aeroflot.ru/flightstatus.asmx?disco).
  • Получим ссылку вида ru.aeroflot.webservices. Для удобства переименуем ее в aeroflot.
Теперь можно вызывать сервис:
<%
aeroflot.FlightStatus myFlightStatus = new aeroflot.FlightStatus() ;
aeroflot.Flight[] result = myFlightStatus.Arrival( "SVO1", DateTime.Today.ToString());
foreach ( aeroflot.Flight f in result ){
	Response.Write( f.company ) ;
	Response.Write( f.flight_no ) ;
	Response.Write( "\t" );
	Response.Write( f.airport ) ;
	Response.Write( "\t" );
	Response.Write( f.sched ) ;
	Response.Write( "\t" );
	Response.Write( f.plan ) ;
	Response.Write( "\n<BR>" );
}
%>

Клиент на Perl

Для написания клиентов на Perl мы рекомендуем использовать удобный и простой модуль SOAP::Lite. Для тестирования использовалась версия 0.52. Данная версия уже корректно поддерживает работу с WSDL файлами.(В предыдущей версии была необходимость сохранять WSDL файл вручную и далее выполнять некоторые замены, но с этой версии необходимость в этом отпала).

Во время написания клиента столкнулись только с одним некорректным поведением модуля, а именно, игнорированием типа, описанного в WSDL файле и попыткой автоматического выставления типа. Если происходит вызов метода, которому в качестве значения строкового параметра передается строка, содержащая только число, то в теле SOAP-запроса SOAP::Lite указывает его, как тип int. Что приводит к ошибке вызова из-за несовпадения типов параметров. Возможно, в следующих версиях SOAP::Lite эта ошибка будет исправлена, а пока достаточно принудительно указывать тип аргументов при их вызове.

Программа-клиент выглядит следующим образом:

use SOAP::Lite service =>
'http://webservices.aeroflot.ru/FlightStatus.wsdl';

($DAY, $MONTH, $YEAR) = (localtime)[3,4,5];
$now = sprintf("%02d.%02d.%04d", $DAY, ($MONTH + 1 ), ($YEAR + 1900));

$code = SOAP::Data
-> type ("string")
-> name ("code")
-> value ("SVO1")
;
$date = SOAP::Data
-> type ("string")
-> name ("date")
-> value ($now)
;

$flights = Arrival($code, $date);


foreach $flight ( @$flights ) {
	print $$flight{"company"} . $$flight{"flight_no"} . "\t" . $$flight{"airport"} . "\t"
		. $$flight{"sched"} . "\t" . $$flight{"plan"} . "\n";
}
Как видите, все довольно просто.

Клиент на PHP

Для реализации клиентов на PHP рекомендуем использовать модуль NuSOAP.
В принципе, этот пример мало чем отличается от предыдущего.
<?php

require_once('nusoap.php');
$soapclient = new soapclient('http://webservices.aeroflot.ru/FlightStatus.wsdl','wsdl');

$code = "SVO1";
$date = date("d.m.Y");

$parameters = array($code, $date);
$flights = $soapclient->call('Arrival',$parameters);

foreach ( $flights as $flight ) {
	print $flight['company'] . $flight['flight_no'] . "\t" . $flight['airport'] . "\t" . $flight['sched'] . "\t" . $flight['plan'] . "\n";
}
?>

Клиент на VB 6.0

Работать SOAP на VB 6.0 можно с использованием Microsoft Soap Toolkit 2.0. Если Вы используете только простые типы, то никакой дополнительной работы выполнять не нужно, Soap Toolkit самостоятельно знает, как ему преобразовывать базовые типы в XML и обратно. Но когда возникает необходимость в передаче сложных типов (структур, классов), то тут ему нужно помочь.

Обработка сложных типов в Soap Toolkit
Для передачи сложных типов в Soap Toolkit существуют две возможности:

  • Каждый раз при получении/передаче значений данного типа самостоятельно разбирать XML, работая с объектом типа IXMLDOMNodeList
  • Написать специализированный обработчик типа.
Более удобным является, естественно, второй вариант. Один раз создаем обработчик типа, а потом работаем с ним, как с обычным классом.

Для создания и использования специализированного обработчика типа необходимо:

  • Создать класс, который соответствует передаваемому или получаемому сложному типу.
  • Создать класс-обработчик для этого типа, который будет преобразовывать объекты данного класса в XML и обратно.
  • Создать WSML-файл, в котором настроить соответствие типа и его обработчика.
Описание процедуры создания своих обработчиков есть в документации по Soap Tookit, а также можно для примера использовать обработчик типов для нашего сервиса (см. http://webservices.aeroflot.ru). Для тех, кто будет создавать свои обработчики, рекомендуем использовать код этого обработчика, и создавать свои по аналогии. Хотелось бы обратить внимание на некоторые моменты:
  1. Не используейте один обработчик(mapper) для всех полей, а создавайте свой для каждого поля структуры/объекта и определяйте тип динамически. Тогда, при смене типа данных одного из полей, даже с простого на сложный, ничего изменять не придется, система все отработает автоматически.
  2. Сложные типы в WSDL файле могут включать элементы в строгом(sequence) или в произвольном(all) порядке . Это надо учитывать при инициализации (Set Node = pSchema.selectSingleNode("XSD:sequence/XSD:element[@name='city']"))
  3. Не все поля структуры могут быть обязательными. Поэтому, прежде чем вызвать обработчик поля, проверьте, есть ли значение (pNode.selectSingleNode("city") is nothing). Иначе произойдет ошибка.
После создания специализированного обработчика класса необходимо создать WSML файл.
	<?xml version='1.0' encoding='UTF-8' ?>
	<servicemapping name='FlightStatus'>
	  <service name='FlightStatus'>
		<using PROGID='FlightStatusMapper.AirportMapper' cachable='0' ID='AirportMapper' />
		<types>
		  <type name='Airport' targetNamespace='http://www.aeroflot.ru/' uses='AirportMapper'/>
		</types>
	  </service>
	</servicemapping>
	
Имя сервиса в ServiceMapping, Service берем из раздела service в WSDL файле. В инструкции using указываем, имя своего обработчика и его PROGID, а в инструкции type ставим соответствие типу в WSDL файле имя своего обработчика.

Подготовительная работа завершена и можно осуществлять вызов.

Вызов сервиса
 

		Set SoapClient = CreateObject("MSSOAP.SoapClient")
		SoapClient.ClientProperty("ServerHTTPRequest") = True
		Call SoapClient.mssoapinit("http://webservices.aeroflot.ru/flightstatus.wsdl", _
			"", "", "http://webservices.aeroflot.ru/flightstatus.wsml")

		flights = SoapClient.Arrival("SVO1", CStr(Now))
		For i = 0 To UBound(flights)
			Set odata = flights(i)
			Debug.Print odata.Company & odata.FlightNo & vbTab & _
				odata.Airport & vbTab & odata.Sched & vbTab & odata.Plan


		Next
	

Клиент на ASP

Для работы с сервисом из ASP также необходимо использовать Soap Toolkit. Только в отличие от VB есть одна маленькая неприятность. Дело в том, что в результате вызова функций сервиса возвращается либо массив объектов (Variant/Object), либо массив дат (Variant/Date), как в функции DateList. При этом VBScript работает только с типом данных Variant. Поэтому и обрабатываемый массив должен содержать тип Variant (Variant/Variant). Иначе обращение к любому элементу массива вызовет ошибку «Несовпадение типов».

Проблема решается очень просто. Добавим в специализированный обработчик класса, который мы уже создавали функцию MakeVariantArray, преобразующую переданный ей массив элементов в массив элементов типа Variant.

 

Public Function MakeVariantArray(v) As Variant
	MakeVariantArray = Empty
	On Error GoTo Errors
	Dim i&
	If Not IsEmpty(v) Then
		If UBound(v) >= 0 Then
			ReDim vres(UBound(v)) As Variant
			For i = 0 To UBound(v)
				If IsObject(v(i)) Then
					Set vres(i) = v(i)
				Else
					vres(i) = v(i)
				End If
			Next
			MakeVariantArray = vres
		End If
	End If

	Exit Function
Errors:
	MakeVariantArray = Empty
End Function
Теперь все отличие кода на ASP от кода на VB заключается только в том, что:
  • Надо не забывать приводить тип аргументов (СStr и т.д.).
  • Преобразовывать полученный массив в массив типа Variant.
<%
	Set SoapClient = Server.CreateObject("MSSOAP.SoapClient")
	Set SoapUtils = Server.CreateObject("FlightStatusMapper.Utils")
	SoapClient.ClientProperty("ServerHTTPRequest") = True
	Call SoapClient.mssoapinit("http://webservices.aeroflot.ru/flightstatus.wsdl", _
		"", "", "http://webservices.aeroflot.ru/flightstatus.wsml")

	flights = SoapClient.Arrival(cstr("SVO1"), CStr(Now))
	flights = SoapUtils.MakeVariantArray(flights)
	For i = 0 To UBound(flights)
		Set odata = flights(i)
		Response.Write odata.Company & odata.FlightNo & vbTab & _
			odata.Airport & vbTab & odata.Sched & vbTab & odata.Plan & "<BR>"
	Next
%>

Заключение

Как видите, при наличии небольшого навыка, создание и использование Web Services не представляет особых сложностей. Можно быстро создавать приложения и сервисы, и быть уверенными, что их легко можно будет использовать в приложениях на разных платформах, из разных языков программирования.

Информацию о своих Web-сервисах Вы можете разместить в каталог российских Web-сервисов на GotDotNet.RU.

 

Информация по теме


Может пригодится:


Автор: nick
Прочитано: 9000
Рейтинг:
Оценить: 1 2 3 4 5

Комментарии: (0)

Добавить комментарий
Ваше имя*:
Ваш email:
URL Вашего сайта:
Ваш комментарий*:
Код безопастности*:

Рассылка новостей
Рейтинги
© 2007, Программирование Исходники.Ру