Posting a serialized object with AFNetworking failing - ios

I have a a data object, called DataElement. It contains a string of Base64 converted image bytes, along with a couple of other fields.
I am trying to post this to my wcf service and am getting an error 'Expected status code in (200-299), got 400.
The goal is to post data + an image to the WCF (rest) service, and get a modified image back- an end to end test of what I am working on.
In my post method, if I leave the encoded string empty on the object everything works just fine- but if that string is anything other than empty I get this error.
My WCF service isn't even being hit, it just bombs right to the error. Here is my post method... what am I doing wrong?
- (void)postDataToServer:(NSString*)server dataElement:(DataElement*)dataElement asJson:(BOOL)useJson
{
NSString *urlString = [[NSString alloc] init];
NSData *encodedData;
urlString = [[server copy] stringByAppendingString:#"EchoXml"];
encodedData = [self encodeDataElementAsXml:dataElement];
NSURL *url = [NSURL URLWithString:urlString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:urlString parameters:nil];
[request setHTTPBody:encodedData];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[_responseTextView setText:[NSString stringWithFormat:#"Successfully uploaded file to %#", urlString]];
NSObject *httpResponseObject;
httpResponseObject = [self parseResponseAsXml:responseObject];
if ([httpResponseObject isKindOfClass:[DataElement class]])
{
DataElement *dataElement = (DataElement *)httpResponseObject;
_responseTextView.text = dataElement.DataText;
if (dataElement.DataImageBase64 != nil)
{
UIImage *dataImage = [self getImageFromString:dataElement.DataImageBase64];
self.responseImageView.image = dataImage;
}
}
NSLog(#"Successfully uploaded file to %#", urlString);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// It goes here immediately
[_responseTextView setText:[NSString stringWithFormat:#"Error: %#", error]];
NSLog(#"Error: %#", error);
}];
[operation start];
}
Edit: Sorry the formatting got wonky when I pasted it in...

The important parts of your code are:
NSString* urlString = [server stringByAppendingString:#"EchoXml"];
NSURL *url = [NSURL URLWithString:urlString];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient
requestWithMethod:#"POST" path:urlString parameters:nil];
The actual URL that AFNetorking requests is the AFHTTPClient's base URL, with the specified path appended to it.
Your mistake is that you are specifying the same urlString again.
So, if urlString is http://your.server.com/EchoXml, then the effective URL that you're requesting is http://your.server.com/EchoXmlhttp://your.server.com/EchoXml. As you see, that doesn't work.
Fix your base URL and path to be something more appropriate. Since you didn't say what URL you are trying to access, it's hard to give much more detail. Maybe server should be the base URL, and EchoXml the path?

I know its bad form to answer my own question- but I found and fixed the problem. Bottom line, the code I was using above is fine- maybe not optimal (as Kurt pointed out) but it does what it is supposed to do.
The problem was on on my WCF service- REST service requests by default have a 65k upload limit. I reconfigured the service to allow large file uploads and everything is good.

Related

deal with special characters in NSURL

i have this code to url of image , i have little problem with my url it contain \u and xcode consider it as special character So i escape it by adding \u in my url
but when i pass it
fullPath = [NSString stringWithFormat:#"http:\\www.school-link.net\\uploads\\%#",image_url];
NSLog(#"file path %#",fullPath);
//i try escape space by this code it dosnt work
//fullPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc]initWithString:fullPath];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
AFHTTPRequestOperation *posterOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
posterOperation.responseSerializer = [AFImageResponseSerializer serializer];
[posterOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#", responseObject);
image_view.image = responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image request failed with error: %#", error);
}];
[[NSOperationQueue mainQueue] addOperation:posterOperation];
[posterOperation start];
it give me error , any ideas
thank you all
image request failed with error: Error Domain=NSURLErrorDomain Code=-1000 "bad URL" UserInfo=0x8f9f900 {NSUnderlyingError=0x9a94cf0 "bad URL", NSLocalizedDescription=bad URL}
Your URL is malformed. Right now you have:
http:\\www.school-link.net\\uploads\\%#
Those backslashes should instead be forward slashes. Ex:
http://www.school-link.net/uploads/%#
Now you can append image_url to the URL, and presuming image_url doesn't cause the URL to be malformed, your request will go through.
Use NSUTF8String encoding. It will solve the problem. For example
NSString *strURL = [NSString stringWithContentsOfURL:yourURL encoding:NSUTF8StringEncoding error:NULL];
A line using this is commented in your code.

Using AFNetworking for Web Service data retrieval

Learning steeply on this one so I'd say it's 99% my mistake somewhere.
Trying a simple procedure for my first app.
I want to get data from the web service and output something to a label.
I'm a .NET developer so web service has been done and returns JSON data as expected in a browser.
{"GetVenuesForBrandResult":[{"brandID":0,"createdDate":"/Date(-62135596800000+0000)/","venueID":2,"venueName":"HMV Records"}]}
After looking at SO, decided to use AFNetworking, version 2.0, loaded as a pod.
So everything works in terms of getting the data but 2 questions please;
1) the output view in Xcode shows data thus;
GetVenuesForBrandResult=({brandID = 0;createdDate = "/Date(-62135596800000+0000)/";venueID = 2;venueName = "HMV Records";
}
);
Is this just XCode5 formatting for display purposes? And will my data be json in real terms?
2) How do I consume this?
Tried checking for isKindOfClass to be an nsarray or nsdictionary and these if conditions are never met. Which led me to suspect Q1 is the issue.
Your help to an Xcode newbie is appreciated.
as per Paul's excellent hint, the relevant part of my code is here;
the web service link is hard coded for now for test purposes
NSString *urlstring = #"http://hiddenurl/shopBeaconService/Service1.svc/GetVenuesForBra nd/1";
//NSString *str2 = self.email.text;
//NSString *str3 = #"/";
//NSString *str4 = [self.password text];
//NSString *urlstring = [NSString stringWithFormat:#"%#%#%#%#",str1,str2,str3,str4];
NSURL *url = [NSURL URLWithString:urlstring];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation,id responseObject)
{
NSLog(#"%#",responseObject);
// NSDictionary *item = [responseObject objectForKey:#""];
// NSString *title = [item objectForKey:#"venueName"];
//NSLog(#"venue= %#",title);
} failure:nil];
[operation start];
The response object when you use the AFJSONResponseSerializer is an NSDictionary. What you are seeing in your log is the output from [responseObject description] - a method that prints a description for logging purposes.
Ray Wenderlich has a good tutorial on AFNetworking

AFJSONRequestOperation / AFHTTPClient fetching old data

So all I'm trying to do is serve up a .json file somewhere (I've tried both my own personal server, and also on AppFog), then performing a GET request using AFJSONRequestOperation on the iOS platform. My code to do this request is as follows:
AFHTTPClient *aclient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]];
NSURLRequest *request = [aclient requestWithMethod:#"GET"
path:#"voucher.json"
parameters:nil];
AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", (NSString *)responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[op start];
The problem here is that say the .json file initially contained an array of 2 strings. The above code would fetch these objects just fine. Then if I edited the .json file with another 3 strings, rerunning the code would continue to fetch only the 2 old strings.
Only 10-15 minutes after I made the change to the .json file will a refresh give me the updated data. At first I thought this was a caching issue, but setting [client setCachingPolicy:] didn't make any difference.
This issue is present whether I host my .json file on my static server, or running in a Node express server on AppFog. It just keeps returning an old version of the resource requested.
I'm really puzzled about this: so any help would be greatly appreciated!
Make sure the url request is not cacheing the data. I had to implement the following so I would get new json data each time a fetch was made.
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
This is likely a caching issue - you could change the cache policy on the request or append something onto the query string, like the current time.
To do this you could use something like:
[aclient requestWithMethod:#"GET"
path:[NSString stringWithFormat:#"voucher.json?%#", [NSDate date]]
parameters:nil];

POST Request returning unwanted cached results (AFNetworking)

I'm using AFNetworking to make POST requests from a shared "authenticator" class that passes in a user's username and password. Here is the POST request that I make:
NSURL *url = [NSURL URLWithString:#"https://www..."];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
// params
NSDictionary* dict = #{#"loginName": username,
#"password": password,
#"serviceName": #"...",
#"serviceURL": #"...",
#"action": #"..."};
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request = [httpClient requestWithMethod:#"POST" path:#"..." parameters:dict];
request.cachePolicy = NSURLRequestReloadIgnoringCacheData;
request.timeoutInterval = 30.0;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", operation.responseString);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"login failed");
}];
[operation start];
It works very well for the first login and everything returns as expected. When I attempt to login with a different username/password, I see that the output of the operation.responseString is the exact same output as the first login.
Does anyone know why it is returning the output from the first login? I feel that the response is a cached response and I had added the following to try to prevent the return of cached information:
request.cachePolicy = NSURLRequestReloadIgnoringCacheData;
I have set breakpoints to see that the username and password in the NSDictionary for the parameters are the new username/password combination.
The string literals are not manipulated in anyway as well and are the same in every POST request. The elipses are for privacy and are placeholders for strings with semantic meaning.
Try instead
request.cachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
because the NSURLRequestReloadIgnoringCacheData only ignores local cache data and not caches out on the network.
Edit: As Steve Madsen points out below, this was not the real problem, and, in general, responses to POST requests are not cached in any case. The actual problem was that the program didn't log out between two logins, by mistake. But we did fix it in the end!
I have same problem, and fixed finally. Using the method:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0
diskCapacity:0
diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
from this blog post :How Does Caching Work in AFNetworking? : AFImageCache & NSUrlCache Explained
Try it
[request setHTTPShouldHandleCookies:NO];

OAuth 2 bearer Authorization header

With an update to the client's API the HTTPBasicAuthication method has been replace with a OAuth2 Bearer Authorization header.
With the old API I would do the following:
NSURLCredential *credential = [NSURLCredential credentialWithUser:self.account.username
password:self.account.token
persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *space = [[NSURLProtectionSpace alloc] initWithHost:kAPIHost
port:443
protocol:NSURLProtectionSpaceHTTPS
realm:#"my-api"
authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
But this will not work with the Bearer header.
Now normally I would just add the header my self by adding it like so:
NSString *authorization = [NSString stringWithFormat:#"Bearer %#",self.account.token];
[urlRequest setValue:authorization forHTTPHeaderField:#"Authorization"];
But the problem with this solutions is that the API redirect most of the calls to other URLs, this has to do with security.
After the NSURLRequest gets redirected the Authorization header is removed from the request and since I'm unable to add the Bearer method to the NSURLCredentialStorage it can't authenticate any more after being redirected.
What would be a good solutions? I can only think to catch the redirect and modify the NSURLRequest so it does include the Bearer header. But how?
Well after much research I found out that I will just have to replace the NSURLRequest when a call is redirected.
Not as nice as I would like it to be, but is does work.
I used AFNetworking and added the redirect block, then check wether the Authorization header is still set if not I create a new NSMutableURLRequest and set all the properties to match the old request (I know I could have just created a mutable copy):
[requestOperation setRedirectResponseBlock:^NSURLRequest *(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse) {
if ([request.allHTTPHeaderFields objectForKey:#"Authorization"] != nil) {
return request;
}
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc] initWithURL:request.URL cachePolicy:request.cachePolicy timeoutInterval:request.timeoutInterval];
NSString *authValue = [NSString stringWithFormat:#"Bearer %#", self.account.token];
[urlRequest setValue:authValue forHTTPHeaderField:#"Authorization"];
return urlRequest;
}];
I'm using AFNetworking Library
Find AFHttpClient.m and you have a method
- (void)setAuthorizationHeaderWithToken:(NSString *)token {
[self setDefaultHeader:#"Authorization" value:[NSString stringWithFormat:#"Token token=\"%#\"", token]];
}
replace this method with the following or if you need it for back compatibility keep it an add with a different name and use that name
- (void)setAuthorizationHeaderWithToken:(NSString *)token {
[self setDefaultHeader:#"Authorization" value:[NSString stringWithFormat:#"Bearer %#", token]];
}
then make the request with oauth access token. (Following is a GET method service)
NSURL *url = [EFServiceUrlProvider getServiceUrlForMethod:methodName];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setAuthorizationHeaderWithToken:#"add your access token here"];
[httpClient getPath:#"" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//
}];
Updated
Use Oauth2 Client on AFNetworking written by matt
https://github.com/AFNetworking/AFOAuth2Client
If you happen to be having this issue with Django rest framework and the routers the problem might be related to the trailing slash being clipped by the NSUrlRequest. if the trailing slash is clipped then django will have to redirect your request, to avoid this you can use Trailing_slash = True like this
router = routers.DefaultRouter(trailing_slash=False)
That way not your authorization header nor your parameters will get lost.
Hope this saves somebody some time.

Resources