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

 

Using the Web Services Core Framework and CFNetwork to Access Remote SOAP Services

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 settings
NSURL *url = [NSURL URLWithString:@"http://localhost:8888/"];
NSString *method = @"echo";
NSString *namespace = @"http://localhost:8888/";

// SOAP request params
NSDictionary *params = [NSDictionary dictionaryWithObject:@"foobar"
                                                   forKey:@"param"];
NSArray *paramOrder = [NSArray arrayWithObject:@"param"];

// SOAP request http headers -- some SOAP server impls require even empty SOAPAction headers
NSDictionary *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 request
WSMethodInvocationRef 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 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];

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 creds
while (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 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 = @"example";
NSString *password = @"example";

// create custom http request with auth creds
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);

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

Listing 7: Send SOAP request and print results

// send SOAP request again
result = (NSDictionary *)WSMethodInvocationInvoke(soapReq);

NSLog(@"result: %@", result);

For More Information

ADC Reference Library: Web Services Core Programming Guide

Published: 2007-03-08

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值