argument-passing from iOS to WCF service - objective-c

I need to pass information from the application to the server, specifically a and b in text format. Here is the code the WCF service:
public class iOSService
{
// To use HTTP GET, add [WebGet] attribute. (Default ResponseFormat is WebMessageFormat.Json)
// To create an operation that returns XML,
// add [WebGet(ResponseFormat=WebMessageFo rmat.Xml)],
// and include the following line in the operation body:
// WebOperationContext.Current.Outgoin gResponse.ContentType = "text/xml";
[OperationContract]
public void DoWork()
{
// Add your operation implementation here
return;
}
// Add more operations here and mark them with [OperationContract]
[OperationContract]
public string iOSTest2(string a, string b)
{
string res = "";
try
{
res=(int.Parse(a) + int.Parse(b)).ToString();
}
catch (Exception exp)
{
res = "Not a number";
}
return res;
}
}
After receiving a and b, the server adds them, and sends back their sum.
And here is my code to send parameters to the server of an iOS application:
- (IBAction)test:(id)sender {
NSArray *propertyNames = [NSArray arrayWithObjects:#"23", #"342", nil];
NSArray *propertyValues = [NSArray arrayWithObjects:#"a", #"b", nil];
NSDictionary *properties = [NSDictionary dictionaryWithObjects:propertyNames forKeys:propertyValues];
NSMutableArray * arr;
arr=[[NSMutableArray alloc]initWithObjects:properties, nil];
NSLog(#"%#",arr);
NSError * error;
NSData *jsonData2 = [NSJSONSerialization dataWithJSONObject:arr options:NSJSONWritingPrettyPrinted error:&error];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.mathforyou.net/iOSservice.svc/iOSTest2"]];
NSString *jsonString = [[NSString alloc] initWithData:jsonData2 encoding:NSUTF8StringEncoding];
[request setValue:#"appliction/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData2];
NSLog(#"JSON String: %#",jsonString);
NSError *errorReturned = nil;
NSURLResponse *theResponse =[[NSURLResponse alloc]init];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&errorReturned];
if (errorReturned) {
//...handle the error
NSLog(#"error");
}
else {
NSString *retVal = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",retVal);
}
}
But the server answers me the following message:
"ExceptionDetail":null,"ExceptionType":null,"Message":"The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.","StackTrace":null
And here is the logs from Xcode:
2013-03-01 17:38:28.657 2231233122[3442:c07] (
{
a = 23;
b = 342;
}
)
2013-03-01 17:38:28.659 2231233122[3442:c07] JSON String: [
{
"a" : "23",
"b" : "342"
}
]
2013-03-01 17:38:29.054 2231233122[3442:c07] {"ExceptionDetail":null,"ExceptionType":null,"Message":"The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.","StackTrace":null}
P.S. I asked a friend to help me, but since he does not know objective-c wrote a program just for C++ and the code works, so the problem is not in the server. Here is the code:
string data = "{\"a\":\"560\",\"b\":\"90\"}";
byte[] bd = Encoding.UTF8.GetBytes(data);
HttpWebRequest wr = (HttpWebRequest)HttpWebRequest.Create("http://www.mathforyou.net/IOSService.svc/iOSTest2");
wr.ContentType = "application/json";
wr.Method = "POST";
wr.ContentLength = bd.Length;
Stream sw = wr.GetRequestStream();
sw.Write(bd, 0, bd.Length);
sw.Close();
HttpWebResponse resp = (HttpWebResponse)wr.GetResponse();
Stream s = resp.GetResponseStream();
byte[] bres = new byte[resp.ContentLength];
s.Read(bres, 0, bres.Length);
string ans = Encoding.UTF8.GetString(bres);
Console.WriteLine(ans);
Please help, I'm worn out.

If you copied and pasted the code, it looks like you might have misspelled your Content-Type value. It should be application/json, you have it as appliction/json.
Also, I'm not sure if this matters, but you're also not setting the content length explicitly. I am not sure if setHTTPBody: does that for you.

I'm not sure if you still need this, but try the following:
[request addValue:#"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setValue:jsonString forHTTPHeaderField:#"json"]; //<-- I added this line
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData2];

Related

Replace " in string with \"

I am trying to send a string to the server. I encode it and set it as the body of an HTTP request using
[request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
Where body is the string.It has to be in json format.
For example
body = [NSString stringWithFormat:#"{\"emailBody\":\"%#\"}",string] ;
should be valid
But i accept string from user and it may contain double quotes.Therefore i have to escape double quotes(") in it.
For example if want to send just one "
{\"emailBody\":\"\\\"\"}
(harddcoded) returns positive response from server.
So i would like to create such a string from the original string.I tried the following
string = [string stringByReplacingOccurencesOfString:#"\"" withString:#"\\\\\""];
But it did not work.I got \" in my test email.Thats as far as i have been able to get.
Am I taking the right approach ??I would appreciate it if someone would point me in the right direction.Thanks
You should generate your JSON directly from a dictionary. That'll take care of all the encoding automatically for you:
NSDictionary *body = #{#"emailBody": string};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:body options:0 error:&error];
if (!jsonData) {
NSLog(#"Got an error: %#", error);
} else {
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[request setHTTPBody:jsonString];
}

Why i get timeout erros when page is not available

I use NSString method initWithContentsOfURL:usedEncoding:error: to get content of some page. I notice, when page that i try to access is not exist this method is executed long time and then fail with timeout error. I tried to use NSURLRequest and NSURLConnection classes for the same purposes, but get the same result - execution long time and then timeout error.
When i try to open the same page in browser, i get response more quickly and it returns page is not available error.
It looks like cocoa methods don't do a dns resolution for page name, or they have longer timeout for that operation.
So my question, does cocoa method that i use do dns resolve? How to do that if they didn't?
Samples of code i use:
NSURL* url = [NSURL URLWithString:#"http://unexisting.domain.local"];
NSError* err = nil;
NSString* content = [NSString stringWithContentsOfURL:url usedEncoding:nil error:&err];
if (err) {
NSLog(#"error: %#", err);
} else {
NSLog(#"content: %#", content);
}
NSURL* url = [NSURL URLWithString:#"http://unexisting.domain.local"];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
NSURLResponse* response = nil;
NSError* err = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if (err) {
NSLog(#"error: %#", err);
} else {
NSString* content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"content: %#", content);
}
Thanks!
Gene M. answered how to do that with using of SCNetworkReachability. Here is sample code:
bool success = false;
const char *host_name = [#"stackoverflow.com"
cStringUsingEncoding:NSASCIIStringEncoding];
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL,
host_name);
SCNetworkReachabilityFlags flags;
success = SCNetworkReachabilityGetFlags(reachability, &flags);
bool isAvailable = success && (flags & kSCNetworkFlagsReachable) &&
!(flags & kSCNetworkFlagsConnectionRequired);
if (isAvailable) {
NSLog(#"Host is reachable: %d", flags);
}else{
NSLog(#"Host is unreachable");
}
It's definitely good practice to monitor the reachability (device connectivity) and react to it as noted above.
You can also implement the NSURLConnectionDelegate protocol and its methods such as
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"**ERROR** %#", error);
// Respond to error
}
If you're not connected you should get an NSURLConnection error code of 999 and NSURLErrorCancelled = -999 and/or a NSURLErrorNotConnectedToInternet = -1009 if you're not connected, etc. At least you'd have a report back of what's going on.
Docs:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html

JSON Payload doesnt seem to be sending

My problem I'm pretty positive is simple, I must just be missing something.. just not sure what.
I can send GET and POST for granular elements (this=that kind of stuff), but a web service call I need to send data too, takes a raw JSON block, with no "key"
Heres the method I wrote:
-(NSData *)execute {
// Smart Chooser ?
if(PostData.count >0 || Payload != nil)
[self setMethod:UPLINK_METHOD_POST];
else
[self setMethod:UPLINK_METHOD_GET];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.connectionUrl
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
if([UPLINK_METHOD_GET isEqualToString:self.connectionMethod])
[request setHTTPMethod:#"GET"];
else
[request setHTTPMethod:#"POST"];
NSString *gData = [self compileGetData];
NSString *pData = [self compilePostData];
// if we have get data, set it into the URL string
if(GetData.count > 0) {
[self setURLWithString:[[self.connectionUrl absoluteString] stringByAppendingString:[#"?" stringByAppendingString:gData]]];
[request setURL:self.connectionUrl];
}
// if we have post data, set it in the body
if(PostData.count > 0) {
const char *bytes = [[NSString stringWithString:pData] UTF8String];
[request setHTTPBody:[NSData dataWithBytes:bytes length:strlen(bytes)]];
}
// Override any post data if a payload is already defined.
if(Payload != nil) {
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"URL : %#", request.URL);
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if(err != nil)
NSLog(#"here was an error: %#", err);
return responseData;
}
-(NSDictionary *)executeAsJSON
{
NSData *responseData = [self execute];
NSError *e;
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&e];
}
Ok SO, the way this thing works, is that it automatically sets whether the request is POST or GET depending on the data provided in the GetData, PostData, and Payload vars.
The request is GET by default, but turns into POST if PostData or Payload have anything in them.
The compileGetData and compilePostData mostly just bring back formatted strings with arrays of information combined, nothing special there.
But thats not where the problem is.
See, "Payload" overrides anything "PostData" had in it. If you had provided PostData elements into the class, it would just be overridden by a provided Payload if that does exist.
I needed to provide this to demonstrate the "workarea" as it exists right now, its not linearly provided information.
This is the area of interest:
// Override any post data if a payload is already defined.
if(Payload != nil) {
//const char *plbytes = [[NSString stringWithString:Payload] UTF8String]; // this didn't work
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]]; // inline, doesn't work either
}
When I say "doesnt work", what I mean is, im getting back an error JSON array from the webservice that basically means "hey, wheres the payload?". If the request is not POST it comes back as a general error, so thats all working, the URL is then obviously correct.
I've used RESTConsole for Chrome to test the webservice to make sure its working properly, and it does.
I've also checked through the debugger the exact payload im sending, i copy+pasted that into RESTConsole, and it works there.
I'm.. honestly at a loss here...
Try using a web proxy like Charles or Wireshark (I personally preferr Charles due to it's ease of use, it's a 30-day trial though) and monitor the request you make from RESTConsole and the one you make from your app and see if they look the same.
Check any headers, line returns and anything else that looks different.
That's the best I can think of to start with

Upload audio - http streaming

I'm trying to upload a linear file under http in streaming mode.
The idea is do this steps simultaneously,
1) Thread 1: record an audio file and store it in a temp file
2) Thread 2: Take n bytes from temp file and send it to an http server.
How can I write an http stream?, On CFHTTPStream I did not see write methods, only read :s
Do I need use sockets?
Thanks!!!
My actual code is
CFWriteStreamRef stream;
NSString *strUrl = #"myurl";
NSURL *url = [[[NSURL alloc] initWithString:strUrl] retain];
CFStringRef requestMethod = CFSTR("GET");
CFHTTPMessageRef message= CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, (CFURLRef)url, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Content-Type"),
CFSTR("multipart/form-data"));
stream = ?? //CFReadStreamCreateForHTTPRequest(NULL, message);
CFRelease(message);
//other headers...
if (CFWriteStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue) == false)
{
NSLog(#"Error");
return NO;
}
//
// Open the stream
//
if (!CFWriteStreamOpen(stream))
{
CFRelease(stream);
NSLog(#"Error");
return NO;
}
CFStreamClientContext context = {0, self, NULL, NULL, NULL};
CFWriteStreamSetClient(stram, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
RSWriteStreamCallBack,
&context);
CFWriteStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
The solution is create an subclass of NSInputStream, and implement the methods open, close, read, hasBytesAvailable and don't forget - (NSStreamStatus)streamStatus.
Last method is invoked from http to know if we are open, close or we was finished (NSStreamStatusAtEnd) to send (there are others status, but this are the most important).
I use a tmp file like buffer because I have to send a lot of data, but, perhaps, a data memory buffer could be better.
Finally I implement other class where use my custom NSInputStream, here is the code:
NSURL *url = [NSURL URLWithString:#"url"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"POST"];
//set headers if you have to do for example:
NSString *contentType = [NSString stringWithFormat:#"multipart/form-data"];
[req setValue:contentType forHTTPHeaderField:#"Content-Type"];
//Create your own InputStream
instream = [[CustomStream alloc] init];
[req setHTTPBodyStream:instream];
//I remove instream later
NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:NO];
[aConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[aConnection start];

ASIFormDataRequest - iOS Application. How do I RETRIEVE a post variable

So I have this url that leads to a .php
So far I managed to retrieve every single thing except the actual XML that I want. the XML is stored in a variable called _xml.
if($this->outMethod=="" || $this->outMethod=="POST") //Default to POST
{
$_POST["_xml"] = $_xml;
}
So I've already set the outMethod to POST but I don't understand how to retrieve the value within _xml.
- (void)grabURLInBackground
{
NSLog(#"grab url in background");
NSURL *url = [NSURL URLWithString:#"xxxxxxxxxxx"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:#"POST" forKey:#"outMethod"];
[request setPostValue:#"1" forKey:#"Entity_ID"];
[request setDelegate:self];
[request startAsynchronous];
NSLog(#"end of grabUrlInBackgroun");
}
don't worry the URL is right I just don't want to post it.
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSLog(#"A");
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
if(responseString)
{
NSLog(#"responseData is not null");
}
NSLog(#"response string: %#", responseString);
//NSLog(#"%#", responseString);
}
What I get back is that the request is good, but there is no response in responseString. This is because my php does not want to print out any of the XML on screen in HTML but it stores the result in the variable _xml sent via post "$_POST["_xml"] = $_xml
My question is, how do I get back that xml variable? Isn't there a method available within the ASIHTTPRequest library? I am using ASIFormDataRequest class not ASIHTTPRequest.
You have to print you variable in the php-file:
if($this->outMethod=="" || $this->outMethod=="POST") //Default to POST
{
echo $_xml;
}
A HTTPRequest (and the ASIFormDataRequest as well) isn't interested in any variables you declare in your *.php file. It only returns the string you actually print.