How to handle the response for Feedly Authentication? - ios

Feedly API Documentation
I'm trying to take advantage of Feedly APIs in my testing iOS app, but unfortunately it seems too great a confusion to me, a newbie to coding in general.
I encountered a problem at the very beginning.
The first step required is the Authentication, as shown in the link above. So I write the code below:
NSDictionary *queryParams = #{#"response_type" : #"code",
#"client_id" : clientID, //some NSString
#"redirect_uri" : redirectURL, //#"https%3A%2F%2Flocalhost/"
#"scope" : #"https://cloud.feedly.com/subscriptions"
};
AFHTTPClient *httpClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"https://developer.feedly.com/"]];
[httpClient getPath:#"/v3/auth"
parameters:queryParams
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"response: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"HTTP Requset Operation Error! %#", error);
}];
It appeared that I succeeded in getting the response when I ran the code, since the response was logged. However, the output was like this:
<3c21444f 43545950 45206874 6d6c3e0a 3c68746d 6c206c61 6e673d22 656e223e 0a3c6865 61643e0a 20203c6d 65746120 68747470 2d657175 69763d22 436f6e74 656e742d 54797065 2220636f 6e74656e 743d2274 6578742f 68746d6c 3b206368 61727365 743d5554 462d3822 202f3e0a 20203c6d 65746120 68747470 2d657175 69763d22 436f6e74 656e742d 4c616e67 75616765 2220636f 6e74656e 743d2265 6e2d7573 22202f3e 0a20203c 6d657461 20687474 702d657 ......(the list goes on and on)>
I'm really clueless about what type this is and how I can use it. Is it the response mentioned in the documentation?
Here is the part related to handling the response:
Handling the response
The response will be sent to the redirect_uri specified in the request. If the user approves the access request, then the response contains an code and the state parameter (if included in the request). If the user does not approve the request the response contains an error message. All responses are returned to the web server on the query string, as shown below:
An error response:
https://your.redirect.uri/feedlyCallback?error=access_denied&state=state.passed.in
A code response
https://your.redirect.uri/feedlyCallback?code=AQAA7rJ7InAiOjEsImEiOiJmZWVk…&state=state.passed.in
What does it mean "All responses are returned to the web server on the query string" and how can I access the returned parameters?(This seems to be the part where I'm stumped)
Thanks for anyone who can lead me out of this mess and give me faith to continue coding.
Plus I'm really fuzzy about how to cope with these web service APIs and the whole url-query/response thing. I'd be vastly grateful if someone can name a book or some article from which I can learn something regarding the way this mechanism works.

The response will be sent to the redirect_uri specified in the request.
Check the response url:
https://your.redirect.uri/feedlyCallback?error=access_denied&state=state.passed.in
Notice the feedlyCallback? part and query strings error and state
I don't know what language you are using, but for PHP: you should create feedlyCallback.php and set php server to feed files without .php extension,
in php files, you can acces query strings with $_GET['error'] and $_GET['state']
Just google for things you have problems with. No idea what query stings mean? http://en.wikipedia.org/wiki/Query_string

Related

Order of Parameters in a POST in RestKit

I have configured RestKit successfully so I can send out POST messages to the device I'm working with.
I have confirmed that when I use Chrome's Postman that the format of the message is correct.
I need to send......
id=87654321&content={"ts":1396215675,"payload":{"ssid_pass":"blah"}}&t=12345678
So in Chrome's postman, it goes out correct. But when I use RestKit postObject
[manager postObject:self path:#"/tom" parameters:#{#"id" : K_IDVALUE, #"content": myPayload, #"t":K_TVALUE, } success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Successful Post!");
The order the device sees is wrong. It sees
content={"ts":1396215675,"payload":{"ssid_pass":"blah"}}&id=87654321&t=12345678
The device really wants the content to be between the id and t tags of the POST request. Is there a way to force RestKit to take the parameters as is? It appears that the message is being alphabetized (which I assume is happening at serialization).
Is there a way to force RestKit to take the parameters as is?
What you are passing into RestiKit:
#{#"id" : K_IDVALUE, … }
is a NSDictionary, which has no concept of ordering of its elements.
Your only chance then is that RestKit is using the mapping you specify to determine the order of the keys. I cannot check now into RestKit sources to confirm this hypothesis, but you could just try it out (or simply verify what is your mapping current ordering).

Dealing with unexpected response content types with AFNetworking / AFHTTPRequestOperation

I'm using AFNetworking to process HTTP requests in my iOS app. I have run into a stumbling block. I cannot be certain of what the response content type will be, but you have to set the response serializer BEFORE the request is processed. This means I could do an API request, expecting an image back, but actually there's some authentication error, so the server returns a JSON-formatted response instead.
Here's my code:
AFHTTPRequestOperation* op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setResponseSerializer:[AFJSONResponseSerializer serializer]]; // ??????
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSDictionary* result = (NSDictionary*) responseObject;
onSuccess((NSArray*) result);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
onFailure(error);
}];
[op start];
As you can see, I've had to set the expected content type implicitly by setting the responseSerializer to [AFJSONResponseSerializer serializer]. So if I get something else back, it causes an error, even though I may still wish to parse an process that response when dealing with the error.
So my question is, should I just use the standard AFHTTPResponseSerializer, examine the response status code and then process the response body manually (as json, xml, html an image etc)?
Set the accepted content types you want on the serialiser with acceptableContentTypes:
AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
serializer.acceptableContentTypes = [NSSet setWithArray:#[#"text/plain", #"text/html"]];
[op setResponseSerializer:serializer];
From the docs:
By default, AFJSONSerializer accepts the following MIME types, which
includes the official standard, application/json, as well as other
commonly-used types:
application/json
text/json
You don't have to use AFJSONResponseSerializer, as you can create your own serializer as long as it conforms to the AFURLResponseSerialization protocol.
If you have JSON responses but XML error responses, you could just subclass AFHTTPResponseSerializer and do your own processing in there.
You could also use AFCompoundResponseSerializer to parse different response types just going through the serializers you give it.
Your API is a little unusual: if you aren't authorized, it should just use an HTTP 401 response, not JSON. But there's plenty of unusual API's out there and I bet you don't have control over this one.
The fix is straightforward:
Make an implementation of AFURLResponseSerialization that just acts as a proxy, and assign that one as the serializer for your request. When the response comes in, have it take a quick look at the data and then instantiate and call the right serializer.

AFNetworking 2.0 - unexpected NSURLErrorDomain error -1012

We ran into the following issue with our app that uses AFNetworking 2.0.
When using AFHTTPRequestOperationManager's GET method, we got an error NSURLErrorDomain code -1012. The request used HTTPS and the server does not require user authentication. The request never reached the server by the way.
We have run several tests and this is the first time the error was produced and we are wondering how this error can get produced because it does not seem relevant.
Setup of AFHTTPRequestOperationManager :
httpOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:
[NSURL URLWithString: HTTPS_URL)]];
httpOperationManager.responseSerializer =
[AFXMLParserResponseSerializer serializer];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled: YES];
GET REQUEST
AFHTTPRequestOperation *op =[httpOperationManager GET:
[NSString stringWithFormat:SOME_PATH]
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//code to setup NSXMLParser ...
}
failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error %#", [error localizedDescription]);
}];
I think you already solved the problem, but if you are trying to authenticate in a server that doesn't have a valid certificate you have to set YES for property allowInvalidCertificates in your AFHTTPRequestOperationManager object:
[yourManager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"your_username" password:#"your_password"];
[yourManager.securityPolicy setAllowInvalidCertificates:YES];
Also, as #a1phanumeric said, it can be necessary to include this line:
[yourManager.securityPolicy setValidatesDomainName:NO];
Cheers.
NSURLErrorDomain -1012 is NSURLErrorUserCancelledAuthentication. (See the error code list and search for -1012.)
You state, "the server does not require user authentication". But this error would not be called if that were true.
Possible causes:
Your server is erroneously requesting authorization (a server bug)
The URL formed with HTTPS_URL and SOME_PATH is not what you expect, and some other server is requesting authorization
Some intermediary (like a proxy server, or an access point) is requiring authorization.
Some debugging tips:
Set breakpoints inside the AFNetworking implementation to see which URL is being hit
Configure AFHTTPRequestOperationLogger so you can see the actual request body and response in your console log
Make the same request with curl or Advanced Rest Client and observe the server's response
Side note: I think [NSString stringWithFormat:SOME_PATH] is pointless - why not just use SOME_PATH?

How to do a JSON encoded GET request with AFNetworking-2.0?

I'm trying to use a pattern similar to this post which describes doing a PUT/POST for RESTful API, but for a GET.
My original code looks almost the same, except I used a GET: keyword. I quickly discovered that rather than sending a JSON body, it instead url encodes the parameters. This is not mentioned in the documentation of the AFJSONSerializer class. You have to go to the superclass documentation (AFHTTPSerializer) and read through its properties, where you'll find the one about HTTPMethodsEncodingParametersInURI. By default that set is populated with HEAD, GET, and DELETE. So for those types of requests, the JSON serializer apparently reverts to its parent class for behavior?
So I put together the following code:
AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.requestSerializer.HTTPMethodsEncodingParametersInURI = [NSSet set];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername: currentUser() password: currentPassword()];
[manager
GET: #"https://172.16.214.214:44321/trees"
parameters: [NSDictionary dictionary]
success:^(NSURLSessionDataTask* task, id responseObject){
NSLog(#"Response: %#", responseObject);}
failure:^(NSURLSessionDataTask* task, NSError* error){
NSLog(#"Error: %#", error);}];
The line that sets HTTPMethodsEncodingParametersInURI = [NSSet set] is intended to let me get JSON encoded parameters like I wanted for the GET too. Unfortunately, I see nothing at the server when I use this and get the following in my error console:
2013-12-10 10:11:14.149 myValve[957:60b] Error: Error Domain=NSURLErrorDomain Code=-1005 "The
network connection was lost." UserInfo=0x17e5d4b0
{NSErrorFailingURLStringKey=https://172.16.214.214:44321/trees,
NSErrorFailingURLKey=https://172.16.214.214:44321/trees, NSLocalizedDescription=The network
connection was lost., NSUnderlyingError=0x17e53240 "The network connection was lost."}
What am I still missing?
The rationale behind AFNetworking's behavior here is probably most concisely explained in the Stack Overflow question, HTTP GET with request body. One of those answers quotes Ray Fielding:
... In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.
So, yes, you can send a body with GET, and no, it is never useful to do so.
The HTTP/1.1 spec defines GET to "retrieve whatever information (in the form of an entity) is identified by the Request-URI" and does not contemplate information to be included in the body of the request. So the use of the body in GET request technically might not be prohibited, but, at best, it is non-standard. AFNetworking's choice not to support it is not entirely surprising.
So, you might not want to put JSON in the body for a GET requests. One would generally add the parameters to the URL. If you want to send JSON in the body of the request, then do a POST.
Sounds like it can't connect to the server, have you tried to make a call outside iOS?
For reference I normally make a JSON request like this:
NSURL *url = #"https://172.16.214.214:44321/trees";
NSData* data = [NSData dataWithContentsOfURL: url];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

why is RestKit changing my response content-type?

In short: I try to fetch data form the server with the content-type of the http request header set as #"text/html.. but for some reason RestKit changes that to application/JSON
Explanation:
If I were to make this request using just AFNetworking.. things work like a charm.. this is what my AFNetworking code looks like:
AFHTTPClient *client = [AFHTTPClient alloc] initWithBaseURL:
[NSURL URLWithString:kApiBaseUrl]];
singleton.parameterEncoding = AFJSONParameterEncoding;
[singleton setDefaultHeader:#"Accept" value:#"text/html"];
[client getPath:getPath parameters:nil success:successCallback failure:failureCallback];
If I use that exact same client and attach it to
MyClient *client = [MyClient getSingleton]; //MyClient is instantiated as above
self.objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
self.objectManager.managedObjectStore = self.managedObjectStore;
// this should have already been done by my client, but putting
// it here just to be sure
[self.objectManager setAcceptHeaderWithMIMEType:#"text/html"];
[[RKObjectManager sharedManager] getObjectsAtPath:kGradesPath
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// handle success
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// handle failure
}];
the error i get is:
restkit.network:RKObjectRequestOperation.m:576 Object request failed:
Underlying HTTP request operation failed with error: Error
Domain=org.restkit.RestKit.ErrorDomain Code=-1016 "Expected content type {(
"application/x-www-form-urlencoded",
"application/json"
)}, got text/html" UserInfo=0x8f7acd0
digging into the subject.. i put a break point in managedObjectRequestOperationWithRequest, then I checked the acceptableContentTypes of HTTPRequestOperation created, and it's nil! So i'm assuming that RestKit is just putting its own default acceptable content types.. i just don't know where and how to prevent it. ideas?
p.s. I don't have control over the server, so I can't change it's content-type header to application/JSON
Update:
It turns out that in RKObjectRequestOperation.m it gets the mime-type from [RKMIMETypeSerialization registeredMIMETypes];(line 354).. and so in RKMIMETypeSerialization.hthere is the method:
/**
Registers the given serialization class to handle content for the given MIME Type identifier.
MIME Types may be given as either a string or as a regular expression that matches the MIME Types for which the given serialization should handle. Serializations are searched in the reverse order of their registration. If a registration is made for an already registered MIME Type, the new registration will take precedence.
#param serializationClass The class conforming to the RKSerialization protocol to be registered as handling the given MIME Type.
#param MIMETypeStringOrRegularExpression A string or regular expression specifying the MIME Type(s) that given serialization implementation is to be registered as handling.
*/
+ (void)registerClass:(Class<RKSerialization>)serializationClass forMIMEType:(id)MIMETypeStringOrRegularExpression;
how do I use this to register a text/html content-type?
RestKit generally expects a single MIMEType (JSON) for its response data. However, you can tell it to handle other types like text/plain and text/html using the method you found and in my experience it's been pretty handy. Adding this to my RestKit config (which I do in my app delegate) allows me to accept both application/json and text/html as response data content-types.
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/html"];
In my case, this is also helpful because Jersey - the web services framework the backend team at my company uses - defaults the content-type of empty payloads to text/plain, which triggers failure blocks unless I've specifically registered for that MIMEType.
Hope this helps.
I use change const value to type that recive from server api like this
NSString * const RKMIMETypeJSON = #"text/html";
if recived text structure same as JSON this approach works perfect

Resources