IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  SOA и Web-сервисы  >

Вызов SOAP Web-сервисов с помощью Ajax: Часть 1. Построение клиента Web-сервисов

developerWorks
Опции документа
PDF format - Fits A4 and Letter

PDF - Fits A4 and Letter
184KB

Загрузить Adobe® Reader®

Опции документа, требующие включения JavaScript, не отображаются

Обсудить

Исходные тексты примера


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: средний

Джеймс Снелл, разработчик программного обеспечения, IBM

11.09.2009

Реализация основанного на Web-браузере клиента SOAP Web-сервисов с использованием шаблона проектирования Ajax (Asynchronous JavaScript and XML)

Данная статья, первая в короткой серии, описывает реализацию межплатформенного, основанного на JavaScript, клиента SOAP Web-сервисов на базе шаблона проектирования Web-приложений под названием Ajax (Asynchronous JavaScript and XML).

Технология Ajax, получившая известность благодаря использованию в популярных сервисах для Web-приложений, таких как GMail, Google Maps, Flickr и Odeo.com, предоставляет Web-разработчикам способ для повышения полезности и функциональности Web-приложений посредством асинхронного обмена XML-сообщениями. Представленная в этой статье библиотека Web Services JavaScript Library расширяет фундаментальные механизмы, лежащие в основе шаблона проектирования Ajax, реализуя поддержку вызова Web-сервисов на базе SOAP (так называемых «SOAP Web-сервисов»).

Web-сервисы в браузере

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

Одним из таких механизмов, одинаково работающих во всех браузерах, является API-интерфейс XMLHttpRequest, который лежит в основе шаблона проектирования Ajax. Механизм XMLHttpRequest подробно описан в статье Филиппа Маккарти (Philip McCarthy), опубликованной на сайте developerWorks. XMLHttpRequest – это Javascript-объект, используемый для выполнения асинхронных HTTP-запросов. В указанной статье представлена диаграмма последовательности (рисунок 1), позволяющая понять место XMLHttpRequest-объекта в шаблоне проектирования Ajax (в разделе Ресурсы приведена ссылка на полный текст этой статьи).


Рисунок 1. Диаграмма последовательности Ajax (автор: Филипп Маккарти)
Рисунок 1. Диаграмма последовательности Ajax (автор: Филипп Маккарти)

Эта диаграмма показывает, как именно функционирует объект XMLHttpRequest. Часть JavaScript-кода, исполняющегося внутри Web-браузера, создает экземпляр LHttpRequest-объекта и функцию, которая служит в качестве асинхронного обратного вызова. Затем скрипт использует объект XMLHttpRequest для выполнения HTTP-операции с сервером. После получения запроса осуществляется вызов функции обратного вызова. Внутри этой функции может осуществляться обработка возвращаемых данных. Если эти данные представлены в формате XML, то объект XMLHttpRequest автоматически осуществляет разбор этих данных с помощью встроенных механизмов обработки XML-данных.

К сожалению, основная трудность подхода на основе Ajax заключается именно в деталях того, каким образом XMLHttpRequest-объект осуществляет автоматический разбор XML-данных. Для примера предположим, что запрашиваемые мною данные представляют собой так называемый SOAP Envelope (конверт SOAP), содержащий элементы из нескольких разных пространств имен XML Namespace, и я хочу получить значение атрибута >attr, принадлежащего элементу yetAnotherElement (листинг 1).


Листинг 1. SOAP Envelope с несколькими пространствами имен
                
<s:Envelope 
  xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <s:Header/>
  <s:Body>
    <m:someElement xmlns:m="http://example">
      <n:someOtherElement 
        xmlns:n="http://example" 
        xmlns:m="urn:example">
        <m:yetAnotherElement 
          n:attr="abc" 
          xmlns:n="urn:foo"/>
      </n:someOtherElement>
    </m:someElement>
  </s:Body>
</s:Envelope>

В браузерах Mozilla и Firefox извлечение значения атрибута attr представляет собой достаточно простую процедуру (листинг 2).


Листинг 2. Метод для извлечения значения атрибута attr в браузерах Mozilla и Firefox (не работает в браузере Internet Explorer)
                
var m = el.getElementsByTagNameNS(
  'urn:example',
  'yetAnotherElement')[0].
    getAttributeNS(
      'urn:foo',
      'attr');
alert(m); // displays 'abc'

Несколько слов о безопасности

Вследствие некоторых вполне реальных проблем безопасности возможности XMLHttpRequest-объекта в большинстве Web-браузеров по умолчанию ограничены – ему разрешается взаимодействовать только с теми ресурсами и сервисами, которые размещены в том же домене, что и просматриваемая пользователем Web-страница. Например, если я в настоящее время просматриваю страницу, размещенную по адресу http://example.com/myapp/, то XMLHttpRequest-объекту разрешается получать доступ только к ресурсам, размещенным в домене example.com. Эта предосторожность необходима для предотвращения несанкционированного доступа потенциально злонамеренного приложения к защищаемой информации. Поскольку представленный в этой статье клиент Web-сервисов базируется на XMLHttpRequest-объекте, описанное ограничение аналогичным образом применяется и к Web-сервисам, которые вы сможете вызвать.

Если вам необходимо получить доступ к Web-сервисам, расположенным в другом домене, то вы можете использовать следующие два решения:

  • Цифровая подпись скрипта JavaScript. Посредством цифровой подписи вы «говорите» Web-браузеру, что вашему скрипту JavaScript можно доверять, т.е. он не станет осуществлять какой-либо злонамеренной деятельности, поэтому ограничение на доступ XMLHttpRequest-запроса к данным должно быть снято.
  • Использование прокси. Более простое решение состоит в передаче всех запросов от XMLHttpRequest-объекта через прокси-ресурс, расположенный в том же домене, что и загруженная страница. Этот прокси-ресурс пересылает указанные запросы на удаленное местоположение и возвращает результаты в браузер. С точки зрения XMLHttpRequest-объекта, это взаимодействие происходит внутри существующей конфигурации безопасности.

К сожалению, приведенный выше код не будет работать в Internet Explorer 6, поскольку в этом браузере функция getElementsByTagNameNS не реализована, а фактически применяется достаточно бесполезный подход – префиксы XML Namespace рассматриваются как к части имен элементов и атрибутов.

Недостаточная поддержка механизма XML Namespaces в браузере Internet Explorer сильно затрудняет «браузеронезависимое» применение XML-форматов, активно использующих пространства имен, таких как SOAP. Для выполнения таких простых операций как извлечение значения атрибута из результата, приходится писать специальный код, обеспечивающий единообразие ожидаемого поведения во всех браузерах. К счастью, такой «специальный» код допускает инкапсуляцию и многократное использование.

Для вызова Web-сервисов изнутри Web-браузера и для надежной обработки SOAP-сообщений вам необходимо сначала осознать проблемы безопасности (см. врезку «Несколько слов о безопасности»). Кроме того, вы должны написать библиотеку JavaScript-скриптов (рисунок 2), которая обеспечит абстрагирование от несогласованности XML-реализаций в различных браузерах. Это позволит вам работать с данными Web-сервисов непосредственно.


Рисунок 2. Вызов Web-сервиса из скрипта Javascript изнутри Web-браузера с помощью библиотеки Web Services JavaScript Library
Рисунок 2. Вызов Web-сервиса из скрипта Javascript изнутри Web-браузера с помощью библиотеки Web Services JavaScript Library

Показанная на рисунке 2 библиотека Web Services JavaScript Library (ws.js) представляет собой набор объектов и вспомогательных функций JavaScript, обеспечивающих базовый уровень поддержки Web-сервисов на основе спецификации SOAP 1.1. В состав библиотеки Ws.js входят следующие объекты:

  • WS.Call: Клиент Web-сервиса, который служит оболочкой для XMLHttpRequest-объекта
  • WS.QName: Реализация XML Qualified Name
  • WS.Binder: Основа для специальных сериализаторов/десериализаторов XML
  • WS.Handler: Основа для обработчиков запросов/ответов
  • SOAP.Element: Базовый элемент SOAP, который служит оболочкой для модели XML DOM
  • SOAP.Envelope: Объект SOAP Envelope, расширяющий SOAP.Element
  • SOAP.Header: Объект SOAP Header, расширяющий SOAP.Element
  • SOAP.Body: Объект SOAP Body, расширяющий SOAP.Element
  • XML: Межплатформенные обслуживающие методы для обработки XML

Ядром библиотеки ws.js является объект WS.Call, предоставляющий методы для вызова Web-сервиса. Именно WS.Call несет основную ответственность за взаимодействие с XMLHttpRequest-объектом и за обработку SOAP-откликов.

Объект WS.Call представляет следующие три метода.

  • add_handler. Добавляет обработчики запросов/обработчики откликов в цепочку обработки. Вызов объектов-обработчиков осуществляется до и после обращения к Web-сервису, что обеспечивает расширяемые возможности обработки до и после обращения.
  • invoke. Посылает заданный объект SOAP.Envelope в Web-сервис, а после получения ответа осуществляет обратный вызов. Рекомендуется применять этот метод при вызове ориентированных на документы Web-сервисов, использующих буквенное кодирование XML.
  • invoke_rpc. Создает конверт SOAP.Envelope, инкапсулирующий запрос RPC-типа, и посылает его в Web-сервис, а после получения ответа осуществляет обратный вызов.

Хотя в общем случае объект WS.Call – это всего лишь тонкая «обертка» поверх XMLHttpRequest-объекта, он действительно выполняет многие полезные действия. К числу этих действий относится и настройка HTTP-заголовка SOAPAction, который требуется в соответствии со спецификацией SOAP 1.1.



В начало


Использование библиотеки ws.js

API-интерфейс, представляемый библиотекой Web services JavaScript Library, достаточно прост.

Объекты SOAP.* (SOAP.Element, SOAP.Envelope, SOAP.Header и SOAP.Body) предоставляют разработчику средства для построения и чтения пакетов SOAP Envelope (листинг 3), благодаря чему достигается абстрагирование от вспомогательных деталей работы с моделью XML DOM.


Листинг 3. Построение пакета SOAP Envelope
                
var envelope = new SOAP.Envelope();
var body = envelope.create_body();
var el = body.create_child(new WS.QName('method','urn:foo'));
el.create_child(new WS.QName('param','urn:foo')).set_value('bar');

В листинге 4 показан пакет SOAP Envelope, созданный кодом из листинга 3.


Листинг 4. Построение пакета SOAP Envelope
                
<Envelope xmlns="http://schemas.xmlsoap.org">
  <Body>
    <method xmlns="urn:foo">
      <param>bar</param>
    </method>
  </Body>
</Envelope>

Если создаваемый вами пакет SOAP Envelope является запросом в стиле RPC, то элемент SOAP.Body предоставляет эффективный метод set_rpc (листинг 5), который сконструирует все тело запроса из таких компонентов как заданное имя операции, массив входных параметров и URI-идентификаторы, закодированные в стиле SOAP.


Листинг 5. Построение пакета RPC-Request Envelope
                
var envelope = new SOAP.Envelope();
var body = envelope.create_body();
body.set_rpc(
  new WS.QName('param','urn:foo'),
  new Array(
    {name:'param',value:'bar'}
  ), SOAP.NOENCODING
);

Каждый параметр передается внутрь в виде структуры JavaScript-объектов со следующими ожидаемыми свойствами:

  • name. Строка или объект WS.QName; определяет имя параметра. Обязательное свойство.
  • value. Значение параметра. Если тип значения не относится к категории простых типов данных (строка, целое число и т.д.), то должен быть указан объект WS.Binder, способный сериализовать это значение в соответствующую XML-структуру. Обязательное свойство.
  • xsitype: Имя WS.Qname, идентифицирующее тип параметра XML Schema Instance Type (например, если xsi:type=”int”, то xsitype:new WS.Qname(‘int’,’http://www.w3.org/2000/10/XMLSchema’)). Опциональное свойство.
  • encodingstyle.: URI-идентификатор в стиле SOAP, используемый текущим параметром. Опциональное свойство.
  • binder: Реализация WS.Binder, способная сериализовать данный параметр в XML. Опциональное свойство

Например, для задания параметра с именем "abc", относящимся к пространству XML-имен "urn:foo", со свойством xsi:type="int" и со значением "3" можно использовать следующий код: new Array({name:new WS.QName('abc','urn:foo'), value:3, xsitype:new WS.QName('int','http://www.w3.org/2000/10/XMLSchema')}).

После того как будет построен «конверт» SOAP.Envelope для обращения к сервису, я передам этот конверт в метод invoke объекта WS.Call с целью вызова метода, закодированного внутри конверта: (new WS.Call(service_uri)).invoke(envelope, callback)

В качестве альтернативного варианта для «ручного» построения SOAP.Envelope я могу передать операцию WS.QName, массив параметров и стиль кодирования в метод invoke_rpc объекта WS.Call (листинг 6).


Листинг 6. Использование объекта WS.Call для вызова Web-сервиса
                
var call = new WS.Call(serviceURI); 
var nsuri = 'urn:foo';
var qn_op = new WS.QName('method',nsuri);
var qn_op_resp = new WS.QName('methodResponse',nsuri);  
  call.invoke_rpc(
    qn_op,
    new Array(
      {name:'param',value:'bar'}
    ),SOAP.NOENCODING,
    function(call,envelope) {
      // envelope is the response SOAP.Envelope
      // the XML Text of the response is in arguments[2]
    }
  );

После вызова метода invoke или метода invoke_rpc объект WS.Call создаст обеспечивающий XMLHttpRequest-объект, передаст XML-элементы, содержащие конверт SOAP Envelope, получит и обработает ответ, а затем инициирует функцию обратного вызова.

Для поддержания возможности расширенной пред- и постобработки SOAP-сообщений объект WS.Call позволяет вам зарегистрировать набор объектов WS.Handler (листинг 7). Вызов этих объектов осуществляется для каждого запроса, для каждого отклика и для каждой ошибки на протяжении цикла вызова. Новые обработчики могут быть реализованы посредством расширения JavaScript-объекта WS.Handler.


Листинг 7. Создание и регистрация обработчиков запросов/обработчиков откликов
                
var MyHandler = Class.create();
MyHandler.prototype = (new WS.Handler()).extend({
  on_request : function(envelope) {
     // pre-request processing
  },
  on_response : function(call,envelope) {
     // post-response, pre-callback processing
  },
  on_error : function(call,envelope) {
  }
});

var call = new WS.Call(...);
call.add_handler(new MyHandler());

Максимальную пользу такие обработчики приносят при внесении информации в передаваемые конверты SOAP Envelope или при извлечении информации из этих конвертов. Например, такой обработчик мог бы автоматически вставлять соответствующие элементы адресации Web-сервисов в заголовок SOAP Envelope (см. пример в листинге 8).


Листинг 8. Пример обработчика, добавляющего к запросу заголовок WS-Addressing
                
var WSAddressingHandler = Class.create();
WSAddressingHandler.prototype = (new WS.Handler()).extend({
  on_request : function(call,envelope) {  		
    envelope.create_header().create_child(
        new WS.QName('Action','http://ws-addressing','wsa')
      ).set_value('http://www.example.com');
  }
});

Объекты WS.Binder (листинг 9) осуществляют специальную сериализацию/десериализацию объектов SOAP.Element. Любая реализация WS.Binder обязана предоставлять следующие два метода:

  • to_soap_element. Сериализует объект JavaScript в элемент SOAP.Element. Первый передаваемый внутрь параметр представляет собой значение, подлежащее сериализации. Второй параметр – это элемент SOAP.Element, в который должно быть сериализовано передаваемое значение. Этот метод не возвращает никаких значений.
  • to_value_object. Десериализует элемент SOAP.Element в объект JavaScript. Этот метод должен возвратить объект типа «десериализованное значение».

Листинг 9. Пример реализации WS.Binding
                
var MyBinding = Class.create();
MyBinding.prototype = (new WS.Binding()).extend({
  to_soap_element : function(value,element) {  		
    ...
  },
  to_value_object : function(element) {
    ...
  }
});



В начало


Простой пример

Ниже приведен пример проекта, иллюстрирующего основные функциональные возможности библиотеки Web Services JavaScript Library. Использованный в этой демонстрации Web-сервис (показанный в листинге 10) развернут на сервере приложений WebSphere Application Server и реализует простую функцию Hello World.


Листинг 10. Простой Web-сервис «Hello World» на базе Java
                
package example;

public class HelloWorld {
  public String sayHello(String name) {
    return "Hello " + name;
  }
}

После реализации и развертывания рассматриваемого сервиса на сервере WebSphere Application Server его WSDL-описание (листинг 11) определяет SOAP-сообщение, которое вы должны передать в сервис Hello World.


Листинг 11. Фрагмент кода HelloWorld.wsdl
                
<wsdl:portType name="HelloWorld">
  <wsdl:operation name="sayHello">
    <wsdl:input 
      message="impl:sayHelloRequest" 
      name="sayHelloRequest"/>
    <wsdl:output 
      message="impl:sayHelloResponse" 
      name="sayHelloResponse"/>
  </wsdl:operation>
</wsdl:portType>

С помощью библиотеки Web Services JavaScript Library вы можете реализовать метод, который будет осуществлять вызов сервиса Hello World (листинг 12).


Листинг 12. Использование объекта WS.Call для вызова сервиса HelloWorld
                
<html>
<head>
...
<script 
  type="text/javascript" 
  src="scripts/prototype.js"></script>
<script 
  type="text/javascript" 
  src="scripts/ws.js"></script>
<script type="text/javascript">
function sayHello(name, container) {
  var call = new WS.Call('/AjaxWS/services/HelloWorld'); 
  var nsuri = 'http://example';
  var qn_op = new WS.QName('sayHello',nsuri);
  var qn_op_resp = new WS.QName('sayHelloResponse',nsuri);  
  call.invoke_rpc(
    qn_op,
    new Array(
      {name:'name',value:name}
    ),null,
    function(call,envelope) {
      var ret = 
        envelope.get_body().get_all_children()[0].
          get_all_children()[0].get_value();
      container.innerHTML = ret;
      $('soap').innerHTML = arguments[2].escapeHTML();
    }
  );
}
</script>
</head>
...

После этого вы можете вызвать сервис Hello World посредством обращения к функции sayHello из любого места своего Web-приложения (листинг 13).


Листинг 13. Обращение к функции sayHello
                
<body>
<input name="name" id="name" />
<input value="Invoke the Web Service"
       type="button" 
       onclick="sayHello($('name').value,$('result'))" />
<div id="container">Result:
<div id="result">
</div>
<div id="soap">
</div>
</div>
</body>

Успешный вызов даст результат, показанный на рисунке 3. Исполнение этого примера в браузерах Mozilla, Firefox и Internet Explorer должно приводить к одинаковым результатам.


Рисунок 3. Пример Hello World в браузере Firefox
Рисунок 3. Пример Hello World в браузере Firefox


В начало


Следующие шаги

Библиотека Web Services JavaScript Library может быть использована для интеграции базовых SOAP Web-сервисов в ваши Web-приложения – простым и независимым от браузера образом. В следующей статье этой серии будет описано применение этой библиотеки для вызова более сложных Web-сервисов, основанных на семействе спецификаций WS-Resource Framework, а также будут исследованы способы, посредством которых способности этих Web-сервисов могут быть расширены и интегрированы в то или иное Web-приложение.




В начало


Загрузка

ОписаниеИмяРазмерМетод загрузки
Пример проектаws-wsajaxcode.zip19 KBHTTP
Информация о методах загрузки


Ресурсы

Научиться

Получить продукты и технологии

Обсудить


Об авторе

Джеймс Снелл (James Snell) является членом исследовательской лаборатории IBM WebAhead, занимающейся разработкой опытных образцов стандартов и технологий программного обеспечения, находящегося в стадии создания, для собственных нужд IBM. Его научно-исследовательские интересы охватывают широкий круг направлений современных технологий, а именно Atom, AJAX, REST, Open Source, персональные издательские системы, семантические и ситуационные Web-приложения. Он является активным участником проекта Apache Abdera, находящегося в настоящее время на этапе разработки и нацеленного на создание высокоэффективной и функционально законченной реализации стандартов формата синдикации Atom и протокола публикации Atom.




Выскажите мнение об этой странице


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



 


 


 


Поделиться этой статьей:

забобрить забобрить memori сохранить в memori




В начало


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