AFNetworking 2.0 and authorizing the Imgur API - ios

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");
}];

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 do retrieve information from my API using AFNetworking now that I have my token?

I have successfully retrieved a token with an Authorization request. I now have credentials that read like so:
<AFOAuthCredential accessToken:"someToken" tokenType:"bearer" refreshToken:"someRefreshToken" expiration:"2015-10-21 05:02:22 +0000">
I am now trying to use this token to make a GET request to my API. I am using the following to make my request:
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:kBaseUrl]];
AFOAuthCredential *credential = [AFOAuthCredential retrieveCredentialWithIdentifier:KServiceProviderCredential];
[manager GET:#"/api/users"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
As you can see I haven't actually set my credentials anywhere in this request. Which method would I use for this?
Thanks in advance :)
So this what I was looking for:
[manager.requestSerializer setValue:[NSString stringWithFormat:#"Bearer %#", credential.accessToken] forHTTPHeaderField:#"Authorization"];
Once I make my request I have to set my token for the 'Authorization' header in order to access my api.

AFNetworking 2: Authorization header not included in request

I have an app that uses AFNetworking 2. I am trying to make a request with an Authorization header. Here is the code I have at the moment:
NSURL *baseURL = [NSURL URLWithString:WLAPIBaseURL];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:email password:password];
[manager GET:WLAPIEndpointMe parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
When I make the request, I get a 401 response. I monitored the request with Charles, and it turns out that the Authorization header is not included in the request...
I am sure this is not a problem with the server because when I make the same request with curl from the command-line, it works fine.
So, what am I doing wrong?
I managed to make it work, by using credentials instead of setting the Authorization headers explicitly:
NSURL *baseURL = [NSURL URLWithString:WLAPIBaseURL];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
NSURLCredential *credential = [NSURLCredential credentialWithUser:email
password:password
persistence:NSURLCredentialPersistenceNone];
manager.credential = credential;
[manager GET:WLAPIEndpointMe parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
I still do not understand why the headers were stripped out though...

AFNetworking Empty Request Body

I am trying to make a simple request with the new AFNetworking 2.0 release and I cannot seem to get it to post. I get a response back from the server "Expecting text/json or application/json body" but according to the documentation on AFNetworking's GitHub page, I'm doing everything as I should be. It's also worth mentioning that it appears the operation.request.HTTPBody in the last line of my code always appears to be nil.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *request = #{#"email": self.email.text, #"password": self.password.text};
[manager POST:login parameters:request constructingBodyWithBlock:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"DONE!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed to log in: %#", operation.responseString);
NSLog(#"Here's the request: %#", operation.request.HTTPBody);
}];
According to the documentation
POST:parameters:constructingBodyWithBlock:success:failure
is for multipart POST requests and its default serialized is not JSON.
Creates and runs an AFHTTPRequestOperation with a multipart POST request.
You want to use
POST:parameters:success:failure:
instead, as follows
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *request = #{#"email": self.email.text, #"password": self.password.text};
[manager POST:login parameters:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"DONE!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed to log in: %#", operation.responseString);
}];
Try
- (AFHTTPRequestOperation *)POST:(NSString *)URLString parameters:(NSDictionary *)parameters success:(void ( ^ ) ( AFHTTPRequestOperation *operation , id responseObject ))success failure:(void ( ^ ) ( AFHTTPRequestOperation *operation , NSError *error ))failure
This method don't require the constructBody block. I don't think the block can be nil.

Resources