We have a .NET WCF Service talking to an iPhone app. I'm using wsdl2objc to generate the objc code required for all the SOAP magic. SoapUI is able to add this service's wsdl and send requests correctly.
But the request generated by wsdl2objc complains about this:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Sender</s:Value>
<s:Subcode>
<s:Value xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</s:Value>
</s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="en-US">The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
</s:Text>
</s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
However, If I use the same s:Envelope with SoapUI, the request works correctly. The only difference between the two requests as far as I can see is the header section where wsdl2objc generates this:
SOAPAction:http://xx/AuthenticateDevice
but soapUI generates:
Content-Type: application/soap+xml;charset=UTF-8;action="http://xx/AuthenticateDevice"
I tried to change the code that wsdl2objc generated and replaced this:
[request setValue:soapAction forHTTPHeaderField:#"SOAPAction"];
NSString *contentType = [NSString stringWithFormat:#"application/soap+xml; charset=utf-8;"];
[request setValue:contentType forHTTPHeaderField:#"Content-Type"];
with this:
[request setValue:soapAction forHTTPHeaderField:#"action"];
NSString *contentType = [NSString stringWithFormat:#"application/soap+xml; charset=utf-8;"];
[request setValue:contentType forHTTPHeaderField:#"Content-Type"];
But, I just get a 400 error now. Any help is much appreciated!
Thanks,
Teja.
I had a similar issue that was fixed by changing the envelope schema URL in the wsdl2objc output
I changed this
xmlNsPtr soapEnvelopeNs = xmlNewNs(root, (const xmlChar*)"http://www.w3.org/2003/05/soap-envelope", (const xmlChar*)"soap");
to This:
xmlNsPtr soapEnvelopeNs = xmlNewNs(root, (const xmlChar*)"http://schemas.xmlsoap.org/soap/envelope/", (const xmlChar*)"soap");
and it seemed to fix my WCF web-service calls from iOS.
What I think this means is that my version of wsdl2objc is outputting a SOAP 1.1 envelope when the WCF server seems to want SOAP 1.2
Has anyone else noticed this? Is there another way to configure this rather than change after generating the code?
It appears that I was missing begin quote and end quote tags around the action value which was causing this. Here's the code that worked for me, but I'm not happy with setting the action as a part of the http content-type header. So if someone can come up with a better solution, I'd be happy to give credit:
NSString *contentType = [NSString stringWithFormat:#"application/soap+xml; charset=utf-8; action=\"%#\"", soapAction];
[request setValue:contentType forHTTPHeaderField:#"Content-Type"];
In the WCF services I've worked with I've just passed the value "text/xml" in the Content-Type header and then passed the correct soap action value in a separate SOAPAction header (similiar to the way wsdl2objc was doing it).
Here are a couple things to check:
Is your WCF service using basicHttpBinding - or something else. (I seem to remember reading that bindings other than basicHttpBinding don't work well (or at all) with iOS - but I'm not certain)
What is the correct SOAP action value for this service? In the services I work with it's built using <ServiceNamespace>\<WCFInterfaceName>\<MethodName>
Related
I'm stuck on a SOAP integration project. I need to call some SOAP Web Services from an iPad app.
I have the WSDL, and I need to generate the Objective-C classes to invoke the service calls.
The Mac developer library has an "Example: Calling a SOAP Operation with HTTP Authentication", but the sample code it links to is broken (SOAP_AuthExample.dmg, 404).
The Web Services Core Programming Guide even says (emphasis added):
"Currently, OS X provides no high-level support for extracting SOAP functions from WSDL files. You can use NSXMLParser to read a WSDL file into memory from a URL, however. It is then relatively simple to search the file for a particular method name or to find a URL associated with a service. With a bit more effort, you can extract method names, data types, and parameter names to build a SOAP call. You can then use the Web Services Core framework to make SOAP calls and parse the responses."
I've also found the following three generators; however, the first is throwing an error about some missing files, the second's code generates and endless stream of ARC errors when I try to build (and a refactor is not working), and the third is paywalled.
http://easywsdl.com/
https://code.google.com/p/wsdl2objc/
http://sudzc.com/
Can anyone steer me in the right direction?
I was in your same situation a couple of months ago, and I'm currently working in an iPad app that requires to connect to a SOAP service. I already have the classes, but they are specific for my project and I'm not authorised to share it. However, I'm making a more generic Objective-C class (in any case the final solution for all the SOAP servers) to share it with the world on Github.
First of all you have to set the SOAP message. It has a common structure like this:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
// here comes the header information: credentials, connection information. If needed, of course
</soap:Header>
<soap:Body>
// here comes the request parameters. In my case, these parameters are related to a resource called SOAPAction (That identifies the method to be called. e.g getListOfClients). I don't know if it is the same in all servers
</soap:Body>
</soap:Envelope>
I say to you, the company for which I'm making the app, has provided me with the methods and authentication information. I don't know if this is your case, but you have here a general glance of what to do.
I use AFNetworking to send the request, in this way:
NSString *messageLength = [NSString stringWithFormat:#"%lu", (unsigned long)[msg length]];
NSURL *requestURL = [NSURL URLWithString:self.requestURL];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:requestURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30];
[theRequest addValue:[requestURL host] forHTTPHeaderField:#"Host"];
[theRequest addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue:soapAction forHTTPHeaderField:#"SOAPAction"];
[theRequest addValue:messageLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[msg dataUsingEncoding:NSUTF8StringEncoding]];
// sending the request
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
operation.responseSerializer = [AFHTTPResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *xml = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Take the data master Yoda: %#", xml);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Bad SOAP server!. Error: %#", error.description)
}];
[[NSOperationQueue mainQueue] addOperation:operation];
self.requestURL is the server URL, obviously. If the request was successful, then you have the server's xml response ready to parse. If not, a request error description otherwise.
This page helped me a lot to find out a solution for some issues that I encountered, and it is related to the website http://sudzc.com/ that you quotes above:
http://blog.exadel.com/working-with-ios-and-soap/
I hope this helps in any way.
I am having some trouble using HTTP token authentication with a NSMutableURLRequest. I have done some research and have used the following question: ios managing authentication tokens from NSURLRequest / HTTP request to learn how to set the token in the HTTP header. However the token I need to set isn't just a simple header field with a value. I need to set an http header field (or multiple) to look like this: Authorization: Token token="tokenHere", nonce="def". The following code snippets are examples of things I have tried that have failed:
//failed to authenticate with server
[request setValue:#"tokenHere" forHTTPHeaderField:#"token"];
[request setValue:#"def" forHTTPHeaderField:#"nonce"];
//failed as well
NSDictionary *authToken = [NSDictionary dictionaryWithObjectsAndKeys:
#"tokenHere", #"token",
#"def", #"nonce",
nil];
[request setValue:[NSString stringWithFormat:#"Token %#", authToken] forHTTPHeaderField:#"Authorization"];
//failed again
[request setValue:#"Authorization: Token token='tokenHere', nonce='def'" forHTTPHeaderField:#"Authorization"];
The back end server is programmed in ruby, and is expecting a single header that looks just like the example above. I need some help crafting the value so that it works with the back end.
It's just a guess, since the right answer depends entirely on what the server expects; but, using other auth schemes as a model, the header would look something like this:
NSString *authHeader = [NSString stringWithFormat:#"token=\"%#\", nonce=\"%#\"", #"tokenHere", #"def"];
[myMutableRequest setValue:authHeader forHTTPHeaderField:#"Authorization"];
In other words, the name of the header field is "Authorization" and it's value is comma separated name=value pairs. The only part of your spec that doesn't make sense is the extra mention of 'token', as in
... Token token=...
So i am trying to make an http request using ASIHTTPRequest class , to send a json file to my server. I was given an android code which is functional and was asked to convert it to iOS code.
I cant understand how to add headers on my request.The android code looks something like this :
private static final String CONTENT_TYPE = "Content-Type";
private static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8";
and then :
request.addHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
On iOS i figured out that the code is something like this :
[request addRequestHeader:#"Content-Type" value:#"Content-Type"];
[request addRequestHeader:#"Json-Content-Type" value:#"application/json; charset=utf-8"];
BUT i got a bit confused , cause on android the request seems to be paired and on iOS the requestHeaders are individually stated. Is this a problem? Should they be paired too on iOS. Am i missing something here? Thank you for reading my post :D
What you want is
[request addRequestHeader:#"Content-Type" value:#"application/json; charset=utf-8"];
in the android example, they are just declaring two variables for the header values, they could had simply done something like
request.addHeader("Content-Type", "application/json; charset=utf-8");
which would have had the same effect, so you just need the one line i mentioned above.
also: ASIHTTPREQUEST does not get developed any more (see headline here), so consider using another library for something like this, like AFNetworking
I am using SudzC for SOAP web services in an iOS App. The issue I'm facing is, there's a method which is auto generated, and whose parameters contains a simple XML payload. When i send the XML payload as a NSString to the parameter, it automatically converts the & in the XML payload to & amp;
I'm not using any encoding manually in this process. How do I avoid this? Any help would be appreciated.
Edit -
This is the part of code which is auto generated and handling by request
if(postData != nil) {
[request setHTTPMethod: #"POST"];
[request addValue: #"text/xml; charset=utf-8" forHTTPHeaderField: #"Content-Type"];
[request setHTTPBody: [postData dataUsingEncoding: NSUTF8StringEncoding]];
if(self.logging) {
NSLog(#"%#", postData);
}
}
Thanks
Nithin
Well, the simple answer is you have to explore the code which has been generated by SudzC. However the hint is, When you request or receive response SudzC code is probably sanitising your code somewhere, most probably method name with serializeElement. If it is a XML request then try to jump to several definition starting from your main method and unto Soap Create method(Just a guess, I am not sure how your code works.) Try to find code similar to this and modify it accordingly.
[[self.YourXML stringByReplacingOccurrencesOfString:#"\"" withString:#"""] stringByReplacingOccurrencesOfString:#"&" withString:#"&"]];
I'm having the strangest issue right now with google app engine. I'm sending a POST request from iOS and google app engine instead invokes the GET handler.
I've sandboxed this one situation for testing and can't get it figured out. I have an iOS app that just sends a request. And I've commented out everything on GAE except for the service. The service only logs a parameter and returns.
The iOS app I've tried using two different ways of sending the request. Neither works.
iOS Code:
/*
NSURL * url = [NSURL URLWithString:#"http://beermonster-gngrwzrd.appspot.com/TestParameter"];
ASIFormDataRequest * _fdrequest = [[ASIFormDataRequest alloc] initWithURL:url];
[_fdrequest setPostValue:#"hello" forKey:#"testkey"];
[_fdrequest startAsynchronous];
*/
NSURL * __url = [NSURL URLWithString:#"http://beermonster-gngrwzrd.appspot.com/TestParameter"];
NSMutableURLRequest * __request = [NSMutableURLRequest requestWithURL:__url];
[__request setHTTPMethod:#"POST"];
NSString * post = [NSString stringWithFormat:#"testkey=hello"];
[__request setHTTPBody:[post dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendSynchronousRequest:__request returningResponse:nil error:nil];
My App engine handler:
class TestParameter(webapp.RequestHandler):
def post(self):
logging.debug(self.request.get("testkey"))
self.response.out.write(self.request.get("testkey"))
print self.request.get("testkey")
def get(self):
logging.debug("get")
logging.debug(self.request.get("testkey"))
self.response.out.write(self.request.get("testkey"))
The output in the GAE logs shows the "get" code path which isn't correct.
Any ideas why POST requests would come into GAE as a GET? Is there some configuration in GAE that I missed?
Thanks!
Check the entry in app.yaml for the script that handles "/TestParameter". Does it specify "secure: always"? If it does and you make a non-secure connection you will get a 302 redirecting to the secure version.
To fix this either make your post over HTTPS or remove "secure: always" from the entry in app.yaml.
From what I can tell if you want to send POST requests to GAE. Make sure you do it on https. If you make the request on a non-https attempt, it sends back a 302 redirect to the https version of the request. But if whatever you're using to send the request doesn't correctly handle 302's it might resend the request incorrectly.