Objective-c Send Post request to json-RPC webservice - ios

In iOS i want send a POST request to a json-RPC web service. How can i do this?
i read This Page before and this repo but none of them have helped me.

Take a look at the popular AFNetworking networking library. This provides full networking functionality and very well documented code. Although you can also use Apple's networking APIs for this.
Simple AFNetworking example:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
NSDictionary *params = #{#"q":#"Chicago",
#"units":#"imperial",
#"type":#"like",
#"mode":#"json"
};
[manager GET:#"http://api.openweathermap.org/data/2.5/weather" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Success
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Failure
}];

There is this AFJSONRPCClient, which is a JSON-RPC Client built on AFNetworking.
Hope it helps.

Related

AFNetwoking POST call returns 500 internal server error but server got request parameter

I'm trying to upload my payment success message to my server. Below are my code
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] init];
[manager.requestSerializer setValue:[NSString stringWithFormat:#"Bearer %#",myTokenString] forHTTPHeaderField: #"Authorization"];
AFHTTPRequestOperation *operation = [manager POST:#"MYAPI" parameters:paramsDict success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#",error.localizedDescription);
}];
[operation start];
But I'm getting error code 500 (internal server error). But my server has all the information and API call is success. Can anyone please help me understand why it's entering the error block?
In new AFNetworking version, you don't need a initialize for AFHTTPRequestOperation class to handle request so you just adjust your code as following:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setValue:[NSString stringWithFormat:#"Bearer %#",myTokenString] forHTTPHeaderField: #"Authorization"];
[manager POST:#"MYAPI" parameters:paramsDict success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#",error.localizedDescription);
}];
By default requestSerializer is set to an instance of AFHTTPRequestSerializer, which means that Content-Type of your request will be application/x-www-form-urlencoded.
But if your server requires application/json content type for that api, then you must set to an instance of AFJSONResponseSerializer
E.G.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
Encode your url.
NSString *unescaped = #"http://www";
NSString *escapedString = [unescaped stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
Try with formData
[manager POST:#"API" parameters:paramsDict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
it's AFNetworking 3.0 Method, but you can used same (formData) in AFNetworking 2.x
I had been encountering the same issue with AFNetworking POST as well as sometimes on GET calls but... Sometimes i would get a 500 internal server error although the server received request parameters!! Finally after some research with my web services backend developers, i came to know that its caused due to a DLL misconfiguration on the server side, particularly the System.reflection DLL file. Deleting the file from the server side (if it exists) removes the issue, otherwise copy pasting it back from the Bin (if it doesn't exist already) removes the issue. This is pretty baffling but apparently it fixes it!!
AND YES. IT HAS NOTHING TO DO WITH AFNETWORKING!
The answer assumes Microsoft azure server
Try to see what the detail accept/content type is for android. Do you know if they are using retrofit? Most likely it will have to do with your request or response serializer not matching what server is expecting.
Add the below coding to AFURLResponseSerialization.m file and remove the existing code.
#implementation AFJSONResponseSerializer
self.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json", #"text/javascript",#"application/xml",#"text/html", nil];
return self;

Unit test for AFNetworking Post request

I am trying to write unit test for my project which I use AFNetworking in.I use the following operation for my request:
- (void)testRegisterRequest{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
XCTAssert(Result,"Register failed!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Failure
}
As it is asynchronous it never test my XCTAssert line.
I searched a lot but I couldn't manage to find a tutorial or example for these test cases.Please help me by any tutorial link or hint.
Thanks in advance
There are a couple of things here.
You can use expectations for async tests: Asynchronous Testing With Xcode 6
You can use OHHTTPStubs to avoid unneeded network traffic and server load: Usage Examples
XCTestExpectation is a valid approach. Other testing frameworks offer different ways. For example, we can test a success response using OCHamcrest like this:
- (void)testRegisterRequest
{
__block id response = nil;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
response = responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
assertWithTimeout(5, thatEventually(response), is(notNilValue()));
}
You would probably test the response for its attributes, instead of simply testing that it is not a nil value.

How to make HTTP-POST in objective-c

I want to send a HTTP-POST message to a server. I have searched on the internet and there where only very old posts. THis was the best one: Simple http post example in Objective-C?
Does this still works? or are there new ways to make an HTTP-POST message?
You should use AFNetworking lib: https://github.com/AFNetworking/AFNetworking
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"foo": #"bar"};
[manager POST:#"http://example.com/resources.json" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
ASIHTTP Request is undoubtedly a great way for a HTTP-POST communication. This is probably the easiest to use also...
Here's another tutorial for making GET, simple POST and multipart POST requests.
Look up "URL encoded HTTP POST requests" :)

AFNetworking ASPXAUTH Cookies AFHTTPRequestOperationManager

I am trying to work with a .NET server that is returning a ASPXAUTH cookie when logging in. I am definitely getting the cookie back when I watch my network traffic with Charles, but when I inspect [NSHTTPCookieStorage sharedHTTPCookieStorage] I am not finding it contains anything. Listed is my code below. Any help would greatly be appreciated!
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc]
initWithBaseURL:[NSURL URLWithString:#"http://someurl.com/api/"]];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
NSDictionary *parameters = #{#"UserName":#"SomeUserName", #"Password":#"SomePassword"};
[manager POST:#"User/Login" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
I found the solution as... taking the cookie and setting the http header value like so...
[manager.requestSerializer setValue:[NSString stringWithFormat:#".ASPXAUTH=%#", cookie[#"Value"]] forHTTPHeaderField:#"Cookie"];
.NET expects the cookie returned in the above format. Hope this helps anyone.

AFNetworking 2.0 and authorizing the Imgur API

Imgur's API requires that for simply looking up information about an image you just need to authorize your app with your API keys, no need to log in with an account or anything.
It says:
...all you need to do is send an authorization header with your client_id in your requests
Which apparently looks like:
Authorization: Client-ID YOUR_CLIENT_ID
So I tried doing this using AFHTTPRequestOperationManager, which appears to be the replacement for AFHTTPClient in AFNetworking 2.0 as shown below:
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager POST:#"https://api.imgur.com/3/image/1Nf1quS" parameters:#{#"Authorization": #"Client-ID ---"} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure");
}];
Basically tried to send a request with authorization information (I removed my ID for this post). It keeps giving "failure" as a response however.
So I tried playing around with the credential property but NSURLCredential seems to be based off a username and password, and I have neither of those as I just need my client ID.
So I tried a completely different way again:
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
NSMutableURLRequest *request= [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://api.imgur.com/3/image/1Nf1quS"]];
[request addValue:#"Client-ID ---" forHTTPHeaderField:#"Authorization"];
[operationManager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"fail");
}];
This time using the authorization things as a value on the request. But this one actually never even logged anything.
I'm quite new to API use, so I'm really confused what I'm doing wrong.
Edit:
Try this code snippet
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager.requestSerializer setValue:#"Client-ID ---" forHTTPHeaderField:#"Authorization"];
[operationManager GET:#"https://api.imgur.com/3/image/1Nf1quS" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure");
}];
I don't know about Imgur API, but for handling header and stuff, if you are just gonna do a few request to the API, you may add the headers just before the request operation. However, if you need to make API calls in more than one place, I think subclassing AFHTTPRequestOperationManager would be a better way to handle this.
For example you can create a subclass named MRTImgurRequestOperationManager:
#interface MRTImgurRequestOperationManager : AFHTTPRequestOperationManager
+ (MRTImgurRequestOperationManager*)sharedManager;
#end
and then in your implementation file:
+ (MRTImgurRequestOperationManager*)sharedManager
{
static MRTImgurRequestOperationManager *_sharedManager;
static dispatch_once_t _dispatchOnceToken;
dispatch_once(&_dispatchOnceToken, ^{
NSURL *baseURL = [NSURL URLWithString:#"API_URL_HERE"];
_sharedManager = [[MRTImgurRequestOperationManager alloc] initWithBaseURL:baseURL];
});
return _sharedManager;
}
- (id)initWithBaseURL:(NSURL*)url
{
self = [super initWithBaseURL:url];
if (self)
{
// Add headers or other options here
// For example
[self.requestSerializer setValue:#"VALUE" forHTTPHeaderField:#"HEADER_NAME"];
}
return self;
}
This way, you can add/remove HTTP headers without ever changing you code in couple of places. This may help with the testing as well.
So to use this, you would:
#import "MRTImgurRequestOperationManager.h"
and then make your Imgur request with the shared manager.
Edit:
You should use GET, not POST, with the API endpoint you are using
[[MRTImgurRequestOperationManager sharedManager] GET:#"path" parameters:params success:success failure:failure]];
In AFNetworking 2.0 you can set header fields. By using method from AFHTTPRequestSerializer
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
Try some thing like this:
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager.requestSerializer setValue:#"Client-ID ---" forHTTPHeaderField:#"Authorization"];
[operationManager POST:#"https://api.imgur.com/3/image/1Nf1quS" parameters:#{#"Authorization": #"Client-ID ---"} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure");
}];

Resources