AFNetworking 2: Authorization header not included in request - ios

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...

Related

How to perform AFHTTPRequestOperation in AFNetworking 2

I've implemented the JSON parsing using AFNetworking many time in some previous apps as:
NSString *string = [NSString stringWithFormat:#"%#?get_all_data", BaseURLString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//performing parsing here
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//error message displayed here
}
But as of today I started working on an app after a while I came to use AFNetworking again and I installed in using pods so as I write this same code as I use to write before it gives me the error saying Unknown Receiver AFHTTPRequestOperation. Do you mean AFHTTPRequestSerializer?
After searching about it I found that it's AFNetworking 2 or 3 era now and they have somehow changed the scenerio. I didn't find the exact solution on how to implement it now. So can anyone write the code in the answer below that works with the latest version of AFNetworking.
This is the new approach of AFNetworking 3.x to parse data:
NSString *path = #"yourapilink";
NSString *escapedPath = [path stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:escapedPath parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
Hope it help !

AFNetworking http basic authentication gives error "Your API key was not formatted properly"

Trying to use the postmates api, but running into issues with authorization.
To quote their site:
AUTHENTICATION
The Postmates API requires authentication by HTTP Basic Auth headers.
Your API key should be included as the username. The password should
be left empty.
The actual header that is used will be a base64-encoded string like
this:
Basic Y2YyZjJkNmQtYTMxNC00NGE4LWI2MDAtNTA1M2MwYWYzMTY1Og==
I tried using AFNetworking like so
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"(my postmates key)" password:#"" persistence:NSURLCredentialPersistenceNone];
NSMutableURLRequest *request = [manager.requestSerializer requestWithMethod:#"GET" URLString:#"https://api.postmates.com/v1/delivery_zones" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCredential:credential];
[operation setResponseSerializer:[AFJSONResponseSerializer alloc]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
[manager.operationQueue addOperation:operation];
The error message I'm getting is
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSASCIIStringEncoding. Will stop this compatiblity mapping behavior in the near future.
Success: {
code = "invalid_authorization_header";
kind = error;
message = "Your API key was not formatted properly";
}
Instead of creating a credential, you might want to try:
[request setValue:#"Basic Y2YyZjJkNmQtYTMxNC00NGE4LWI2MDAtNTA1M2MwYWYzMTY1Og==" forHTTPHeaderField:#"Authorization"];

How to make an HTTP request with AFNetorking and NSURLRequest?

I am trying to use AFHTTPRequestOperationManager to make an HTTP request. I need to use AFHTTPRequestOperationManager because I want to be able to cancel all operations if necessary. I can't get this working for some reason. The completion blocks aren't called. Am I missing something?
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://twitter.com/%#", username]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"MyUserAgent (iPhone; iOS 7.0.2; gzip)" forHTTPHeaderField:#"User-Agent"];
[request setHTTPMethod:#"GET"];
[self.manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *html = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
if ([html containsString:#"var h = decodeURI(l.hash.substr(1)).toLowerCase();"]) {
completion(YES, nil);
} else {
completion(NO, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completion(NO, error);
}];
This is working code, you need to use GET or POST method there.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *params = #{#"email":emailfield.text};
[manager GET:#"http://example.com/api" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
While everyone else is right -- you should be using the modern AFNetworking constructs instead of the legacy features -- there is a quick way to get done what you're looking to get done.
By the looks of it, the method - (??? *) HTTPRequestOperationWithRequest:success:failure likely returns an AFHTTPRequestOperation. If I'm correct, you just need to actually start the operation. See below for your code, corrected.
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://twitter.com/%#", username]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"MyUserAgent (iPhone; iOS 7.0.2; gzip)" forHTTPHeaderField:#"User-Agent"];
[request setHTTPMethod:#"GET"];
AFHTTPRequestOperation *op = [self.manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *html = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
if ([html containsString:#"var h = decodeURI(l.hash.substr(1)).toLowerCase();"]) {
completion(YES, nil);
} else {
completion(NO, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completion(NO, error);
}];
[op start];
HTTPRequestOperationWithRequest method returns AFHTTPRequestOperation. You have to add it to some operation queue to start it. For example
AFHTTPRequestOperation *operation = [self.manager HTTPRequestOperationWithRequest:request .........
[[NSOperationQueue currentQueue] addOperation:operation];
You can use AFHTTPSessionManager which is a little better than AFHTTPRequestOperationManager and cancel requests using method cancel of NSURLSessionDataTask. You can find some code examples here - AFNetworking 2.0 cancel specific task
Check this will work
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json", nil];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];

AFJSONParameterEncoding in AFNetworking 2.x.x

I am implementing following paypal REST API:
curl -v https://api.sandbox.paypal.com/v1/vault/credit-card \
-H 'Content-Type:application/json' \
-H 'Authorization: Bearer {accessToken}' \
-d '{
"payer_id":"user12345",
"type":"visa",
"number":"4417119669820331",
"expire_month":"11",
"expire_year":"2018",
"first_name":"Joe",
"last_name":"Shopper"
}'
I have successfully implement this api in AFNetworking 1.3.3 with following Code. Where PPWebService is subclass of AFHTTPClient
[[PPWebService sharedClient] setParameterEncoding:AFJSONParameterEncoding];
[[PPWebService sharedClient] setDefaultHeader:#"Content-Type" value:#"application/json"];
[[PPWebService sharedClient] setDefaultHeader:#"Authorization" value:[NSString stringWithFormat:#"Bearer %#", accessToken]];
[[PPWebService sharedClient] postPath:#"vault/credit-card"
parameters:creditCard
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSDictionary *response = [self JSONToObject:operation.responseString];
creditCardId = response[#"id"];
if(creditCardId)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Credit card" message:#"Saved !!" delegate:nil cancelButtonTitle:#"on" otherButtonTitles:nil];
[alert show];
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Credit card" message:error.description delegate:nil cancelButtonTitle:#"on" otherButtonTitles:nil];
[alert show];
}];
I want to use AFNetworking 2.x.x in my project. But I am not able to do it with this new version.
I have subclass AFHTTPRequestOperationManager. I search internet and people suggest me to use AFJSONRequestSerializer. All other code is very similar. But than also I am getting bad request error.
So how can I send raw JSON string in with POST method in AFNetworking 2.x.x?
EDIT
Code for AFNetworking 2.X.X
Error Status : 404 Bad Request
Response :
{"name":"MALFORMED_REQUEST","message":"The request JSON is not well formed.","information_link":"https://developer.paypal.com/docs/api/#MALFORMED_REQUEST","debug_id":"ab3c1dd874a07"}
I am getting proper response by using Postman as shown in following screenshot.
So finally I got answer. I use subclass of AFHTTPSessionManager to implement API in my project. And use it with singleton object. So this is my singleton method.
+ (MASWebService *)APIClient
{
static MASWebService *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
NSURL *baseURL = [NSURL URLWithString:BaseURL];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_sharedClient = [[MASWebService alloc] initWithBaseURL:baseURL sessionConfiguration:config];
_sharedClient.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
_sharedClient.requestSerializer = [AFJSONRequestSerializer serializer];
[_sharedClient.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
});
return _sharedClient;
}
The main key line is setting Request Serializer HTTP Header "Content-Type" to "application/json"
If you are not subclassing AFHTTPSessionManager and using AFHTTPRequestOperationManager for single request than also it will work because AFHTTPRequestOperationManager is also conform to AFURLRequestSerialization protocol as property of AFHTTPRequestSerializer. I have not done it but it should look like this.
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"foo": #"bar"};
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[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);
}];
I hope this will work. Inform me in comment if I am wrong somewhere.
Source : AFNetworking 2.0 POST request working example code snippet
So how can I send raw JSON string in with POST method in AFNetworking 2.x.x?
I had the same issue when trying to use SOAP+XML data with AFHTTPRequestOperationManager. Apparently the request serializer should be subclassed to make provision for your special serialisation needs, but this way seems easier:
NSError *error = nil;
NSData *envelopeData = [NSJSONSerialization dataWithJSONObject:params options:options error:nil];
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:#"POST"
URLString:path
parameters:nil
error:&error];
// In my case, I also needed the following:
// [request setValue:action forHTTPHeaderField:#"SOAPAction"];
[request setHTTPBody:envelopeData];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] init];
operation = [self HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
// parse response here
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
// parse response here
}];
It hijacks the request serialiser that is created by AFHTTPRequestOperationManager, inserts the parameter data into the HTTP body, and then passes this new request into the AFHTTPRequestOperation.

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