using web services core framework and CFNetwork to access remote soap service

來源:互聯網
上載者:User

 

Enterprise developers are excited about the power of Service-Oriented Architectures (SOA) to simplify business-to-business communication, and many large internet sites, like Amazon.com, make their information available via Web services, often based on SOAP (the Simple Object Access Protocol). If you've ever wondered whether you can access these kinds of web services from the Mac, the answer is Yes, and this article shows you how.

The Web Services Core framework is Apple's C-based framework for invoking and implementing SOAP- and XML-RPC-based web services. CFNetwork is Apple's C-based framework for network programming, including HTTP. When combined, WebServices Core and CFNetwork can be used to access remote SOAP-based web services that require HTTP Basic or Digest authentication.

Below is an example of using WebServices Core plus CFNetwork's HTTP authentication features to access a remote SOAP service that requires HTTP Basic authentication.

Download Sample Code Xcode Project (.DMG, 456KB).

This example sends SOAP requests to a tiny Java-based SOAP server program that you can download and run locally on your machine. This simple test server implements only a single SOAP method (called echo) behind HTTP Basic authentication. The test server challenges all SOAP requests, but allows any username and password.

To run the server:

  1. Download and unzip the the test server jar files:

Download Test SOAP Server Jar (.DMG, 444KB).

  1. Open a Terminal.app window and cd into the directory where the test server jar is located.
  2. Type the following command to run the test server on port 8888: >
java -jar SOAP_AuthExampleServer 8888

Begin by declaring or fetching the data that makes up the parameters and other details of the SOAP request. This includes the SOAP method name, method namespace, request parameters, request parameter order and SOAPAction HTTP header.

Listing 1: Declare SOAP request settings

// SOAP request settingsNSURL *url = [NSURL URLWithString:@"http://localhost:8888/"];NSString *method = @"echo";NSString *namespace = @"http://localhost:8888/";// SOAP request paramsNSDictionary *params = [NSDictionary dictionaryWithObject:@"foobar"                                                   forKey:@"param"];NSArray *paramOrder = [NSArray arrayWithObject:@"param"];// SOAP request http headers -- some SOAP server impls require even empty SOAPAction headersNSDictionary *reqHeaders = [NSDictionary dictionaryWithObject:@"" forKey:@"SOAPAction"];

If you are able to include the Foundation framework, it's often easier to work with Cocoa/Objective-C objects as shown in Listing 1, and cast the Cocoa objects to CoreFoundation types when necessary. This approach takes advantage of the CoreFoundation-to -Cocoa toll-free-bridging for basic data types, and makes many common tasks (such as memory management) easier.

Also included in Listing 1 are the declarations of the data used to construct the SOAP request. The SOAP method to be called is a simple method named echo that takes a single string parameter named param. This method echoes the given string parameter (foobar in our example) in the SOAP response.

The URL for this example points to a tiny SOAP server running on port 8888 on localhost. Values for the method namespace and SOAPAction HTTP headers are also declared. Many SOAP server implementations require the presence of an empty SOAPAction header even if the method itself does not specifically require a header value. Creating a dictionary with an empty value for the SOAPAction key and attaching it to the SOAP request forces an empty SOAPAction header to be sent along with the request.

Next, a SOAP request is created from the settings above. WebServices Core represents a SOAP request as aWSMethodInvocationRef type, as shown in Listing 2.

Listing 2: Call function to create SOAP request

// create SOAP requestWSMethodInvocationRef soapReq = createSOAPRequest(url, method, namespace, params, paramOrder, reqHeaders);

In Listing 3, creation of the SOAP request is delegated to the createSOAPRequest function that returns aWSMethodInvocationRef object.

Listing 3: 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) -- some SOAP impls require even empty SOAPAction headers    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;}

Note that in addition to creating the SOAP request, several debug properties of the WSMethodInvocationRef are also set to true. This causes the raw XML contents of the SOAP request and response messages to be included in the result dictionary returned from executing the SOAP request. Viewing this raw XML can be extremely helpful in debugging SOAP client code.

The next step is to invoke the initial SOAP request. Do this using the WSMethodInvocationInvoke function which returns a dictionary containing the SOAP response and other debug information.

Since the service accessed requires HTTP Basic authentication, expect the HTTP headers of the SOAP response to contain authentication challenge information. To respond to the authentication challenge, retrieve the HTTP response from the result dictionary using the kWSHTTPResponseMessage key. The HTTP response is represented by aCFHTTMessageRef object.

Listing 4: Create SOAP request

// invoke SOAP requestNSDictionary *result = (NSDictionary *)WSMethodInvocationInvoke(soapReq);// get HTTP response from SOAP request so we can see response HTTP status codeCFHTTPMessageRef res = (CFHTTPMessageRef)[result objectForKey:(id)kWSHTTPResponseMessage];

Now check for an HTTP response code of 401 or 407 that would signal an HTTP authentication challenge:

Listing 5: Check HTTP response status code

int resStatusCode = CFHTTPMessageGetResponseStatusCode(res);// if response status code indicates auth challenge, attempt to add atuh credswhile (401 == resStatusCode || 407 == resStatusCode) {

If an authentication challenge is returned, you must:

  1. Extract the HTTP response headers from the first SOAP response. These headers contain the necessary auth challenge information.
  2. Create a new CFHTTMessageRef to represent a new HTTP request.
  3. Gather username and password information by prompting the user or by querying some external data source.
  4. Combine the username and password with the auth challenge information from the initial SOAP response to create auth credentials.
  5. Attach the auth credentials to the newly-created HTTP request.
  6. Create a new SOAP request and combine it with the new HTTP request to produce a SOAP request with the necessary auth credentials attached.
  7. Invoke the new SOAP request.

Listing 6: Extract HTTP authentication challenge, and create new SOAP request with credentials

CFHTTPAuthenticationRef auth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, res);// extract details of the auth challenge to display // when prompting the user for username and password informationNSString *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 sourceNSString *username = @"example";NSString *password = @"example";// create custom http request with auth credsNSString *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 requestsoapReq = createSOAPRequest(url, method, namespace, params, paramOrder, reqHeaders);// add HTTP request auth creds to SOAP requestWSMethodInvocationSetProperty(soapReq, kWSHTTPMessage, req);

Finally, send the new SOAP request, and inspect the results:

Listing 7: Send SOAP request and print results

// send SOAP request againresult = (NSDictionary *)WSMethodInvocationInvoke(soapReq);NSLog(@"result: %@", result);
For More Information

ADC Reference Library: Web Services Core Programming Guide

Published: 2007-03-08

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.