Используя платформу ядра веб-сервисов
Фундаментальная задача в использовании веб-сервисов вызывает работу. Это - умеренно сложный процесс. Эта глава содержит два раздела. Первый раздел, Создавая и Вызов Operations, объясняет шаги и обеспечивает иллюстративные фрагменты кода. Второй раздел, Пример: Вызов Работы SOAP с Аутентификацией HTTP, аннотируемый пример с загружаемым примером кода, который можно создать и выполнить.
Создание и вызов операций
Для вызова службы XML-RPC или SOAP на удаленный сервер необходимо создать ссылку вызова, определить параметры и порядок параметра, передачу в настройках, затем вызвать службу. Ссылка вызова включает URL, имя работы и протокол (SOAP или XML-RPC). Параметры и настройки варьируются в зависимости от выбранного протокола — параметры SOAP называют, например, тогда как пронумерованы параметры RPC. SOAP требует пространства имен и обычно заголовка действия SOAP в настройках также. Кроме того, существуют свойства отладки, которые можно установить.
Следующие разделы описывают шаги более подробно.
Ссылка вызова
Вы создаете WSMethodInvocationRef
объект при помощи функции WSMethodInvocationCreate
, который берет URL (CFURLRef
или NSURL
), имя работы (CFStringRef
), и протокол. Протокол является одним из них CFStringRef
константы:
kWSXMLRPCProtocol
kWSSOAP2001Protocol
Передача в URL службы, имени работы и протоколе:
NSURL *url = [NSURL URLWithString:@"http://localhost:8888/"]; |
NSString *methodName = @"echo"; |
WSMethodInvocationRef mySoapRef = WSMethodInvocationCreate((CFURLRef)url, |
(CFStringRef)methodName, |
kWSSOAP2001Protocol); |
Параметры работы
Создайте словарь параметров работы и массива, устанавливающего порядок параметра, затем передайте ссылки на словарь и массив к WSMethodInvocationSetParameters
:
NSDictionary *params = [NSDictionary dictionaryWithObject:@"firstParamName" |
forKey:@"firstParam"]; |
NSArray *paramOrder = [NSArray arrayWithObject:@"firstParam"]; |
WSMethodInvocationSetParameters(mySoapRef, (CFDictionaryRef)params, |
(CFArrayRef)paramOrder); |
Свойства вызова
Вызвать WSMethodInvocationSetProperty
с надлежащими настройками для работы. Например, вызов SOAP требует пространства имен, и заголовок действия SOAP (некоторые реализации SOAP требуют заголовка, даже если это пусто). Вы могли бы также хотеть установить запрос, чтобы позволить перенаправления и включить свойства отладки так, чтобы необработанный XML был включен в возвращенный словарь, вместе с проанализированными данными.
NSString *namespace = @"http://localhost:8888/"; |
NSDictionary *reqHeaders = [NSDictionary dictionaryWithObject:@"" forKey:@"SOAPAction"]; |
WSMethodInvocationSetProperty(mySoapRef, kWSSOAPMethodNamespaceURI, |
(CFStringRef)namespace); |
WSMethodInvocationSetProperty(mySoapRef, kWSHTTPExtraHeaders, |
(CFDictionaryRef)reqHeaders); |
WSMethodInvocationSetProperty(mySoapRef, kWSHTTPFollowsRedirects, |
kCFBooleanTrue); |
// set debug props |
WSMethodInvocationSetProperty(mySoapRef, kWSDebugIncomingBody, |
kCFBooleanTrue); |
WSMethodInvocationSetProperty(mySoapRef, kWSDebugIncomingHeaders, |
kCFBooleanTrue); |
WSMethodInvocationSetProperty(mySoapRef, kWSDebugOutgoingBody, |
kCFBooleanTrue); |
WSMethodInvocationSetProperty(mySoapRef, kWSDebugOutgoingHeaders, |
kCFBooleanTrue); |
Вызов работы
Как только Вы создали ссылку вызова и добавили настройки и свойства, можно вызвать работу. Как правило, Вы тогда используете платформу CFNetwork для проверки состояния сообщения — который избавляет Вас от необходимости пытаться проанализировать неответ в случае проблемы или сетевой ошибки — использование константы kWSHTTPResponseMessage
как идентификатор для запроса веб-сервисов.
NSDictionary *result = (NSDictionary *)WSMethodInvocationInvoke(mySoapRef); |
// get HTTP response from SOAP request so we can see the status code |
CFHTTPMessageRef res = (CFHTTPMessageRef) |
[result objectForKey:(id)kWSHTTPResponseMessage]; |
Вызов работы асинхронно
Поскольку сетевые задержки имеют тенденцию быть непредсказуемо долгими, Вы обычно хотите вызвать операции асинхронно, с помощью WSMethodInvocationScheduleWithRunLoop
, и обеспечение обратного вызова на Вашем цикле выполнения для обработки ответа. Можно перепланировать вызов после того, как он завершится. Установите свое использование обратного вызова WSMethodInvocationSetCallBack
.
Контакт с ответом
Если нет никакой сети, транспорта или отказов веб-сервисов, вызов возвращает словарь ответа метода, или синхронно или на Вашем обратном вызове. (Установите свое использование обратного вызова WSMethodInvocationSetCallBack
). Если Вы знаете то, что ключи для значений, которые Вы хотите, можно получить возвращенные данные из словаря с помощью ключей.
Также можно запросить kWSMethodInvocationResult
и проанализируйте XML сами. Если Вы предпочитаете, можно передать на ожидаемое название параметра результата прежде, чем вызвать работу, когда kWSMethodInvocationResult
будет псевдоним к параметру, который Вы хотите.
Поскольку словарь ответа может содержать необработанный XML, а также десериализованные данные, может быть больше чем один экземпляр данных в словаре.
Добавление Custom Serializers и Deserializers
Если CFTypes для данных, с которыми Вы работаете, имеют соответствующий WSTypes, вызывание работы автоматически сериализирует исходящие данные из словаря в XML и десериализовывает возвращенные данные от XML в словарь. Если необходимо работать с более сложными типами данных, можно записать собственный сериализатор и deserializer обратные вызовы. Когда вызов метода встречается с указанным типом данных в исходящем словаре или входящем ответе XML, эти обратные вызовы вызывают.
Установите использование обратных вызовов WSMethodInvocationAddSerializationOverride
и WSMethodInvocationAddDeserializationOverride
.
Пример: вызов работы SOAP с аутентификацией HTTP
Этот пример вызывает метод SOAP по HTTP. Веб-сервисы обычно требуют аутентификации, таким образом, этот пример отправляет запрос SOAP и, если брошено вызов, аутентифицирует. Можно загрузить пример кода с http://developer.apple.com/internet/webservices/SOAP_AuthExample.dmg.
Как показано в примере кода, начните путем объявления или выборки данных, составляющих параметры и другие подробные данные запроса SOAP, включая имя метода SOAP, пространство имен метода, параметры запроса, порядок параметра запроса и HTTP-заголовок действия SOAP. Метод SOAP в этом случае называют echo
и берет названный отдельный строковый параметр param
. Если вызов успешен, метод повторяет строковый параметр («Я слышу эхо”.) в ответе SOAP. Создайте словарь параметров и их имен для вызовов SOAP (для XML-ВЫЗОВОВ-RPC, используйте порядковые числа для параметров вместо имен), и массив, содержащий параметры в порядке.
// SOAP request settings |
NSURL *url = [NSURL URLWithString:@"http://localhost:8888/"]; |
NSString *method = @"echo"; |
NSString *namespace = @"http://localhost:8888/"; |
// SOAP request params |
NSDictionary *params = [NSDictionary dictionaryWithObject:@"I hear an echo." |
forKey:@"param"]; |
NSArray *paramOrder = [NSArray arrayWithObject:@"param"]; |
// SOAP request http headers -- some server implementations require even empty SOAPAction headers |
NSDictionary *reqHeaders = [NSDictionary dictionaryWithObject:@"" forKey:@"SOAPAction"]; |
Обычно проще работать с объектами Какао, как показано, и бросить объекты к Базовым типам Основы при необходимости. Этот подход использует в своих интересах бесплатное образование моста между Какао и Базовой Основой, и делает много общих задач — таких как управление памятью — проще. Однако можно выполнить те же вызовы от C или C++.
URL для этого примера указывает на работу сервера SOAP порта 8888 на локальном узле. Значения для пространства имен метода и HTTP-заголовков SOAPAction также объявляются. Даже если сам метод в частности не требует значения заголовка, много реализаций сервера SOAP требуют присутствия пустого заголовка действия SOAP. Создание словаря с пустым значением для SOAPAction
ключ и присоединение его к запросу SOAP вынуждают пустой заголовок действия SOAP быть отправленным вместе с запросом.
Затем, запрос SOAP создается из настроек выше. Запрос SOAP представлен как a WSMethodInvocationRef
ввести.
// create SOAP request |
WSMethodInvocationRef soapReq = createSOAPRequest(url, method, namespace, params, paramOrder, reqHeaders); |
Затем, создание запроса SOAP делегировано к createSOAPRequest
функция, возвращающая a WSMethodInvocationRef
объект.
// Custom function to create SOAP request |
WSMethodInvocationRef createSOAPRequest(NSURL *url, |
NSString *method, |
NSString *namespace, |
NSDictionary *params, |
NSArray *paramOrder, |
NSDictionary *reqHeaders) |
{ |
WSMethodInvocationRef soapReq = WSMethodInvocationCreate((CFURLRef)url, |
(CFStringRef)method, |
kWSSOAP2001Protocol); |
// set SOAP params |
WSMethodInvocationSetParameters(soapReq, (CFDictionaryRef)params, (CFArrayRef)paramOrder); |
// set method namespace |
WSMethodInvocationSetProperty(soapReq, kWSSOAPMethodNamespaceURI, (CFStringRef)namespace); |
// Add HTTP headers (with SOAPAction header) |
WSMethodInvocationSetProperty(soapReq, kWSHTTPExtraHeaders, (CFDictionaryRef)reqHeaders); |
// for good measure, make the request follow redirects. |
WSMethodInvocationSetProperty(soapReq, kWSHTTPFollowsRedirects, kCFBooleanTrue); |
// set debug props |
WSMethodInvocationSetProperty(soapReq, kWSDebugIncomingBody, kCFBooleanTrue); |
WSMethodInvocationSetProperty(soapReq, kWSDebugIncomingHeaders, kCFBooleanTrue); |
WSMethodInvocationSetProperty(soapReq, kWSDebugOutgoingBody, kCFBooleanTrue); |
WSMethodInvocationSetProperty(soapReq, kWSDebugOutgoingHeaders, kCFBooleanTrue); |
return soapReq; |
} |
Обратите внимание на то, что в дополнение к созданию запроса SOAP, нескольких свойств отладки WSMethodInvocationRef
объект также установлен в истину. Это вызывает необработанные содержания XML запроса SOAP, и ответные сообщения, которые будут включены в словарь результата, возвратились из выполнения запроса SOAP. Просмотр этого необработанного XML может быть чрезвычайно полезным в отладке клиентского кода SOAP.
Следующий шаг должен вызвать начальный запрос SOAP. Сделайте это использование WSMethodInvocationInvoke
функция, возвращающая словарь, содержащий ответ SOAP и другую отладочную информацию.
Так как служба получила доступ, требует Базовой аутентификации HTTP, ожидайте, что HTTP-заголовки ответа SOAP будут содержать информацию о запросе аутентификации. Для ответа на запрос аутентификации получите ответ HTTP из словаря результата с помощью kWSHTTPResponseMessage
ключ. Ответ HTTP представлен a CFHTTMessageRef
объект.
// invoke SOAP request |
NSDictionary *result = (NSDictionary *)WSMethodInvocationInvoke(soapReq); |
// get HTTP response from SOAP request so we can see response HTTP status code |
CFHTTPMessageRef res = (CFHTTPMessageRef)[result objectForKey:(id)kWSHTTPResponseMessage]; |
Теперь проверьте на Код ответа HTTP 401 или 407, который сигнализировал бы проблему Аутентификации HTTP. Если запрос аутентификации возвращается, Вы должны:
Извлеките заголовки ответа HTTP из первого ответа SOAP. Эти заголовки содержат необходимую информацию о запросе аутентификации.
Создайте новое
CFHTTMessageRef
объект представлять новый Запрос HTTP.Соберите информацию имени пользователя и пароля путем запроса пользователя или путем запросов некоторого внешнего источника данных.
Объедините имя пользователя и пароль с информацией о запросе аутентификации от начального ответа SOAP для создания учетных данных.
Присоедините учетные данные к недавно создаваемому Запросу HTTP.
Создайте новый запрос SOAP и объедините его с новым Запросом HTTP для создания запроса SOAP с необходимыми присоединенными учетными данными аутентификации.
Вызовите новый запрос SOAP.
// get status |
int resStatusCode = CFHTTPMessageGetResponseStatusCode(res); |
// if response status code indicates auth challenge, attempt to add authorization |
while (401 == resStatusCode || 407 == resStatusCode) { |
CFHTTPAuthenticationRef auth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, res); |
// extract details of the auth challenge to display |
// when prompting the user for username and password information |
NSString *scheme = [(NSString *)CFHTTPAuthenticationCopyMethod(auth) autorelease]; |
NSString *realm = [(NSString *)CFHTTPAuthenticationCopyRealm(auth) autorelease]; |
NSArray *domains = [(NSArray *)CFHTTPAuthenticationCopyDomains(auth) autorelease]; |
NSLog(@"Providing auth info for \nscheme: %@\n, realm: %@\n, domains: %@", scheme, realm, domains); |
// Replace with a user prompt or fetch data from remote source |
NSString *username = @"uName"; |
NSString *password = @"pWord"; |
// create custom http request with authorization |
NSString *reqMethod = @"POST"; |
CFHTTPMessageRef req = CFHTTPMessageCreateRequest(kCFAllocatorDefault, |
(CFStringRef)reqMethod, |
(CFURLRef)url, |
kCFHTTPVersion1_1); |
// add auth creds to request. |
Boolean success = CFHTTPMessageAddAuthentication(req, |
res, |
(CFStringRef)username, |
(CFStringRef)password, |
NULL, |
false); |
if (!success) { |
NSLog(@"failed to add auth to request"); |
return EXIT_FAILURE; |
} |
// create a new SOAP request |
soapReq = createSOAPRequest(url, method, namespace, params, paramOrder, reqHeaders); |
// add HTTP request auth creds to SOAP request |
WSMethodInvocationSetProperty(soapReq, kWSHTTPMessage, req); |
// send SOAP request again |
result = (NSDictionary *)WSMethodInvocationInvoke(soapReq); |
NSLog(@"result: %@", result); |
В этой точке консоль должна отразить строку, которую Вы отправили в сервер SOAP. Вы успешно вызвали службу, ответили на вызов Аутентификации HTTP, сериализировали Ваш исходящий запрос и десериализовали ответ.