I created an NSDictionary containing arrays and strings and other client info, using setObject forKey. Everything looks great when I NSLog the data, format is exactly the way its supposed be. I've also converted my NSDictionary to NSData:
NSData *userData = [NSJSONSerialization dataWithJSONObject:userDict options:NSJSONWritingPrettyPrinted error:&error];
What I need to know is how to upload it to the server using POST. I've found the following code snippet to upload a photo. My question is can I simply use my NSDictionary as a parameter (params in request), it's kinda big. If not, how do I send my NSData object userData? I really love AFNetworking and have been using it exclusively for all my download needs. This is my first time uploading an object.
Thanks
NSData *imageData = UIImagePNGRepresentation(pageImage);
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://www.SERVER.com"]];
//You can add POST parameteres here
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
author, #"author",
title, #"title",
nil];
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"POST" path:#"/PATH/TO/WEBSERVICE.php" parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
//This is the image
[formData appendPartWithFileData: imageData name:#"cover_image" fileName:#"temp.png" mimeType:#"image/png"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
//Setup Upload block to return progress of file upload
[operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
NSLog(#"Upload Percentage: %f %%", progress*100);
}];
//Setup Completeion block to return successful or failure
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = [operation responseString];
NSLog(#"response: [%#]",response);
//Code to run after webservice returns success response code
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", [operation error]);
//Code to Run if Failed
}];
[operation start];
I am not sure I fully understand you question, but I will give it a try anyway.
My question is can I simply use my NSDictionary as a parameter (params in request), it's kinda big.
I think so. Just give it a try... in the end, those array will be converted in data sent to the server, so if the server is able to handle it, there should be no problem.
If not, how do I send my NSData object userData?
The data you send should be provided where the code you posted specifies imageData. Keep in mind that if what you are uploading is not meant to be a file (you mention NSData), then you might need to use appendPartWithFormData:name. This, again, depends on your server side.
Hope this clarifies things a bit.
Related
TwitterKit changed the way its email gets retrieved again and now I can't figure out how to retrieve the email from JSON using the new format.
Previously I would just do this:
TWTRShareEmailViewController* shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString* email2, NSError* error) {
NSLog(#"Email %#, Error: %#", email2, error);
But now they've gotten rid of the TWTRShareEmailViewController (as of version 2.0) and I have to do this:
TWTRAPIClient *client = [TWTRAPIClient clientWithCurrentUser];
NSURLRequest *request = [client URLRequestWithMethod:#"GET"
URL:#"https://api.twitter.com/1.1/account/verify_credentials.json"
parameters:#{#"include_email": #"true", #"skip_status": #"true"}
error:nil];
[client sendTwitterRequest:request completion:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
**something should go here**
}];
...but I'm not quite sure how to get the email from the json now.
Any help would be appreciated.
Ok. In the end it was pretty easy. All I had to do was convert the JSON to a string. And from there I could do whatever I wanted to it. So I did it this way:
TWTRAPIClient *client = [TWTRAPIClient clientWithCurrentUser];
NSURLRequest *request = [client URLRequestWithMethod:#"GET"
URL:#"https://api.twitter.com/1.1/account/verify_credentials.json"
parameters:#{#"include_email": #"true", #"skip_status": #"true"}
error:nil];
[client sendTwitterRequest:request completion:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}];
From there you could turn it to a dictionary or array or whatever you want to do with it. Phew! I really need more sleep!
I'm trying to send a JSON request using AFNetworking and have a problem with making values be translated to the json form of {"value": true}. Instead, I'm getting: {"value": 1}
Here's basically how I'm creating the request:
NSMutableURLRequest *request =
[self.httpClient requestWithMethod:#"POST"
path:url
parameters:#{#"value": #YES}];
AFJSONRequestOperation *operation =
[AFJSONRequestOperation JSONRequestOperationWithRequest:request ...];
[operation start];
Am I missing something trivial here? :)
Short answer:
Make sure you are running a recent version of AFNetworking. That's all I can see as the problem based on the code you've provided.
Long answer:
I've tried reproducing the issue you're describing with the most recent versions of AFNetworking and I could not. I dug into AFNetworking to see how the encoding of JSON is done. AFHTTPClient.m:442 uses NSJSONSerialization to encode JSON requests. I came up with the following code to test the issue:
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:#{#"value" : #YES} options:0 error:&error];
NSLog(#"Resulting JSON:\n\n%#\n", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
outputs:
{"value":true}
So #YES should do it. As a note, be sure not to use #(YES) in your code as it will output as a 1 instead of true.
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:#{#"value" : #(YES)} options:0 error:&error];
NSLog(#"JSON:%#", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
outputs:
{"value":1}
With that I went through and tried to figure out how AFHTTPClient need to be configured to send out a bool as 1/0 instead of true/false and could not find any. Here's my networking code.
AFHTTPClient* httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://<SERVER HERE>"]];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
NSMutableURLRequest *jsonRequest = [httpClient requestWithMethod:#"POST" path:#"/" parameters:#{#"value": #YES}];
AFHTTPRequestOperation *jsonOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:jsonRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Success");
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Failure");
}];
[jsonOperation start];
Since #YES is an NSNumber, NSJSONSerialization turns this to 0/1.
I don't think there's a way other than #{#"value": (yesOrNo ? #"true" : #"false")} or using a different serialization class.
For people who might be running into this issue, there's another reason why it might be happening.
Make sure you set the parameterEncoding property of your AFHTTPClient subclass to AFJSONParameterEncoding, otherwise you'll run into the issue of NSNumber's initialization value not being correctly detected, and will see 0s and 1s being output instead by the encoder.
See this for reference as well.
Hope this helps.
In the subclass of HTTPClient. Instead of:
self.responseSerializer = [AFJSONResponseSerializer serializer];
try with:
$self.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
I have the same error, I am sending the #YES but the services give me fail, so I create and string of a json and create an jsonObject like this:
NSString* paramsString = #"{";
NSString* appending = [NSString stringWithFormat:#"\"%#\":%#,", KEY_CHECKED, (checked ? #"true" : #"false")];
paramsString = [paramsString stringByAppendingString: appending];
paramsString = [paramsString stringByAppendingString:#"}"];
id object = [NSJSONSerialization JSONObjectWithData:[paramsString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
Use this object for send the post with AFNetworking
[self postParameters:object];
for me works!
So, I've ran over and over the web in search for anything about sending XML with POST from iPhone app - no luck so far, none!
I'm using in my app KissXML, which I find very easy and useful when it comes to getting XML out of response - but quite opposite when sending XML to server...
Here is my method for connecting and receiving XML. I tried to put NSString containing simply my XML request into body of POST request, but it doesn't work as planned.
-(void)prepareTransaction{
NSLog(#"FXSecondVC: preparing transaction...");
NSString *login = [[NSUserDefaults standardUserDefaults] stringForKey:#"kUsername"];
NSString *password = [[NSUserDefaults standardUserDefaults] stringForKey:#"kPassword"];
NSString *host = [[NSUserDefaults standardUserDefaults] stringForKey:#"kURLServer"];
NSURL *url = [[NSURL alloc] initWithString:host];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[httpClient setAuthorizationHeaderWithUsername:login password:password];
[httpClient registerHTTPOperationClass:[AFKissXMLRequestOperation class]];
NSString *xmlString = #"<RootEl xmlns=\"http://some.url/goes/here\">"
"<Element1>12678967.543233</Element1>"
"<Element2>"
"<string xmlns=\"bla.bla/url\">"
"String content</string>"
"<string xmlns=\"bla.bla/url\">"
"String content</string>"
"</Element2>"
"<Element3>true</Element3>"
"<Element4>String content</Element4>"
"<Element5>1999-05-31T11:20:00</Element5>"
"<Element6>true</Element6>"
"</RootEl>";
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:kServerRequestURL parameters:nil];
[request setHTTPBody:[xmlString dataUsingEncoding:NSUTF8StringEncoding]];
AFKissXMLRequestOperation *operation = [AFKissXMLRequestOperation XMLDocumentRequestOperationWithRequest:request success:^(NSURLRequest *req, NSHTTPURLResponse *resp, DDXMLDocument *XMLDocument){
NSLog(#"[SUCCESS]: XMLDocument: %#", XMLDocument);
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, DDXMLDocument *XMLDocument) {
NSLog(#"error parsing: %#", [error localizedDescription]);
}];
[operation start];
}
This is what I'm getting in response:
2012-11-21 19:40:09.884 FXApp[19662:707] FXSecondVC: preparing transaction...
2012-11-21 19:40:10.011 FXApp[19662:707] error parsing: Expected status code in (200-299), got 400
Am I missing something here? I want to use KissXML, because it the simplest way (at least known to me) to use already prepared XML document in successful response, but if solution requires changing framework - don't hesitate. The priority is to get it working.
I hit dead end - this is driving me crazy, especially it is really urgent matter.
Mystery solved:
it appears that all I had to do was to set Content-Type for xml - which I wasn't doing. Solution found here
Here you go:
[request setValue:#"application/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
In my case, I had to use setValue:#"text/xml" to get the desired JSON response from the server (PHP server).
i.e. I used the following:
[request addValue:#"text/xml" forHTTPHeaderField:#"Content-Type"];
i am working on a project that need to upload files from iphone to wcf service. i dont have experience on both wcf and afnetworking. i've stuck on this step for days and heres the progress that i've made:
WCF Service for uploading files: note that i've copied this code from Codeproject website.
public interface ITransferService
{
[OperationContract]
RemoteFileInfo DownloadFile(DownloadRequest request);
[OperationContract]
void UploadFile(RemoteFileInfo request);
}
public void UploadFile(RemoteFileInfo request)
{
FileStream targetStream = null;
Stream sourceStream = request.FileByteStream;
string uploadFolder = #"C:\upload\";
string filePath = Path.Combine(uploadFolder, request.FileName);
using (targetStream = new FileStream(filePath, FileMode.Create,
FileAccess.Write, FileShare.None))
{
//read from the input stream in 65000 byte chunks
const int bufferLen = 65000;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
{
// save to output stream
targetStream.Write(buffer, 0, count);
}
targetStream.Close();
sourceStream.Close();
}
}
The upload code works good on the client program came with the sourcode, i can upload any size and any type or files through the wcf service.
I've also found that AFNetworking framework is quite popular on ios, so i've decided to use it. heres my code for uploading file:
i've come this far, please help me in this situation. thanks for helping
OK, heres the new information:
Fırst of all, the c# code to upload file to the wcf service (which is working)
protected void Button1_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
System.IO.FileInfo fileInfo =
new System.IO.FileInfo(FileUpload1.PostedFile.FileName);
FileTransferServiceReference.ITransferService clientUpload =
new FileTransferServiceReference.TransferServiceClient();
FileTransferServiceReference.RemoteFileInfo
uploadRequestInfo = new RemoteFileInfo();
using (System.IO.FileStream stream =
new System.IO.FileStream(FileUpload1.PostedFile.FileName,
System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
uploadRequestInfo.FileName = FileUpload1.FileName;
uploadRequestInfo.Length = fileInfo.Length;
uploadRequestInfo.FileByteStream = stream;
clientUpload.UploadFile(uploadRequestInfo);
//clientUpload.UploadFile(stream);
}
}
}
Second: The remotefileinfo class that used to upload file to server:
public class RemoteFileInfo : IDisposable
{
[MessageHeader(MustUnderstand = true)]
public string **FileName**;
[MessageHeader(MustUnderstand = true)]
public long **Length**;
[MessageBodyMember(Order = 1)]
public System.IO.Stream **FileByteStream**;
public void Dispose()
{
if (FileByteStream != null)
{
FileByteStream.Close();
FileByteStream = null;
}
}
}
From all those code i understand that i need to create a request that contains "Filename" "FileLength" and the filedata "FileByteStream". i tried something in the codes but the server gives error 415 when i try to upload image with this code:
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://192.168.2.121:85"]];
UIImage *image = [UIImage imageNamed:#"test.jpg"];
NSData *data = UIImageJPEGRepresentation(image, 0.2);
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
[parameters setObject:#"test.jpg" forKey:#"FileName"];
[parameters setObject:[NSString stringWithFormat:#"%i",data.length] forKey:#"Length"];
NSMutableURLRequest *myRequest = [client multipartFormRequestWithMethod:#"POST" path:#"/webservice/Transferservice.svc/UploadFile" parameters:parameters constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:data name:#"RemoteFileInfo" fileName:#"test.jpg" mimeType:#"image/jpeg"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:myRequest];
[operation
setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", operation.responseString);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.error);
}
];
[[[NSOperationQueue alloc] init] addOperation:operation];
also here is the WSDL Link for the service :
WSDL Link
i really need to do this, thanks for helping again...
Try this for size:
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://192.168.2.121:85"]];
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:#"test.jpg"], 0.2);
NSMutableURLRequest *myRequest = [client multipartFormRequestWithMethod:#"POST" path:#"/webservice/Transferservice.svc/UploadFile" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:data name:#"RemoteFileInfo" fileName:#"test.jpg" mimeType:#"image/jpeg"];
}];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:myRequest] autorelease];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObj) {
NSLog(#"success: %#", operation.responseString);
} failure:^(AFHTTPRequestOperation *op, NSError *error) {
NSLog(#"[Error]: (%# %#) %#", [operation.request HTTPMethod], [[operation.request URL] relativePath], operation.error);
}];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[queue addOperation:operation];
You shouldn't need to pass the name of you file or the size in your post parameters you can pick this up on the receiving form. Don't forget that the name part of the appendPartWithFileData should be used by you server side script to identify the file upload.
All the above is relevant in PHP but should work the same for ASP.NET.
Also like above the link doesn't work.
Cheers
Finally, i've succeded on uploading a file to WCF via filestream. The problem was the previous code was expecting a header and a stream in the body which i couldnt find a way to do that. instead, i've found that i need to write a code that only acceps a stream as parameter, nothing much, and then the filename and other stuff will be done at the serverside.
i've got that upload code for wcf service from this question: WCF service to accept a post encoded multipart/form-data
did exactly whats written in the answer. and in the iphone side, i've setup an inputstream for my operation. heres the code for the AFNetworking for uploading files to the wcf service:
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://192.168.2.121:85"]];
UIImage *image = [UIImage imageNamed:#"test.jpg"];
NSData *data = UIImageJPEGRepresentation(image, 0.2);
NSInputStream *stream = [[[NSInputStream alloc]initWithData:data] retain];
NSDictionary *parameters = nil;
NSMutableURLRequest *myRequest = [client requestWithMethod:#"POST" path:#"/uploadservice/service1.svc/Upload" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:myRequest];
[
operation
setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", operation.responseString);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.error);
}
];
operation.inputStream = stream; //Thats where you put your stream!
[[[NSOperationQueue alloc] init] addOperation:operation];
Thanks everyone for their patience, even your replies wasnt the complete answer, your replies led me to the correct answers...
You're getting a 404, or "Not found" error back from the remote web server.
This probably means you have an incorrect URL path or server name - there's not enough information about the server side setup for us to deduce what the correct URL would be.
I am doing a service in our customer company. And I try to get some information from their server by AFNetWorking (Our customer encourage to use AFNetWorking)
I did some sample using AFNetWorking, and it's work.
But when I use one of our customer URLs to get JSON data, it failed and this is error description:
Error Domain=com.alamofire.networking.error Code=-1011
"Expected status code <NSIndexSet: 0x7e274f0>[number of indexes: 100 (in 1 ranges),
indexes: (200-299)], got 403" UserInfo=0x7b64040 {NSErrorFailingURLKey=<url_hidden_for_stackoverflow>,
NSLocalizedDescription=Expected status code <NSIndexSet: 0x7e274f0>[number of indexes: 100 (in 1 ranges), indexes: (200-299)], got 403}
I try to find out some solution, but I can't fix yet. There's my code:
NSURL *url = [NSURL URLWithString:#"http://example.com/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
//[httpClient setDefaultHeader:#"Accept" value:#"text/json"];
//NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:CONST_KEY_REGISTER_UNIQUE_KEY, CONST_API_KEY_TEXT,nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"path/to/page.json" parameters:nil];
[httpClient release];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSString *status = [JSON valueForKey:#"status"];
if ([status isEqualToString:#"OK"]) {
NSString *uniqueId = [JSON valueForKey:#"uniqueId"];
[UserSettings setWithKey:CONST_PROGRAM_UNIQUE_KEY value:uniqueId];
}
//NSString *message = [json valueForKey:#"message"];
//NSString *error = [json valueForKey:#"error"];
[[LoadingView instance] performSelectorOnMainThread:#selector(removeLoadingView) withObject:nil waitUntilDone:YES];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSString *errorString = [error description];
[[LoadingView instance] performSelectorOnMainThread:#selector(removeLoadingView) withObject:nil waitUntilDone:YES];
}];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[queue addOperation:operation];
Thanks for reading, and any help or reply will be greatly appreciated.
EDIT: As DarkDust said: server deny my access. But I can get data from server by basic connection:
Here is code to get:
NSURL *url = [NSURL URLWithString:#"http://example.com/path/to/page.json"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:CONST_CONNECTION_TIMEOUT];
rssConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
[self performSelectorOnMainThread:#selector(downloadStarted) withObject:nil waitUntilDone:NO];
if (rssConnection != nil) {
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// I can get text here, but it is not JSON format
NSString *content = [NSString stringWithUTF8String:[data bytes]];
}
I wonder why rssConnection can get JSON text and AFHTTPClient can not ?
As reference because of high search result via google...
For others that are looking for the possible error codes retrieved via AFNetworking, consult the apple documentation for URL Loading System Error Codes as these are the same.
NSURLErrorBadServerResponse = -1011
Returned when the URL Loading system receives bad data from the server.
This is equivalent to the “500 Server Error” message sent by HTTP servers.
The server is responding with the HTTP error code 403 which means Forbidden. It denies you access. You need to find out why, for example by reading the server logs (if you can) or asking the server administrator to help you. It might be access restrictions on the server that need to be lifted/modified.
Edit: A HTTP POST is an operation that wants to save something on the server. While the normal GET seems to work just fine according to your edited question, saving is prohibited right now. First thing to do is still examine the server configuration. Additionally, if your URL points to a script (JSP, ASP, whatever) which is the only thing that would make sense in your case you need to examine that to determine why it denies you access (if the server configuration doesn't already deny it, it must be the script).