5

I'm trying to use Diffbot to parse some URLs into the relevant article portion. They have an "Article API" that allows you to submit one link at a time and receive it back, but for speed I'd prefer to use the Batch API which basically allows you to submit a bunch of Article API requests into one big request and get one big response, instead of one at a time.

Here's what the Batch API described in their documentation (that is oddly behind a login wall):

enter image description here

I'm submitting to the Article API as so:

NSURLRequest *request = [[AFDiffbotClient sharedClient]
                             requestWithMethod:@"GET"
                             path:[NSString stringWithFormat:@"article?token=MYTOKEN&fields=url,text,title&url=%@", URL]
                             parameters:nil];

And it's working perfectly. AFDiffbotClient is a singleton combined with AFNetworking to help me make requests easier, and the URL parameter is just the URL of the article I'm looking at. (Perhaps I could be doing that without creating the URL manually, bonus points if anyone could offer tips on that.)

However, with the Batch API, you're supposed to submit (POST) a bunch of these requests as a JSON array. I'm confused how I would go about doing this.


EDIT: I've worked some more on it, and made some progress, but I'm getting a 400 error back. I can't figure out what I'm doing wrong, but I must be along the right path. I'm passing parameters in the POST request with my token and my JSON array, but it still won't work.

[AFDiffbotClient sharedClient].operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;
NSMutableArray *DiffbotRequests = [[NSMutableArray alloc] init];

for (NSDictionary *URLAndID in URLsAndIDs) {
    NSString *articleURL = [URLAndID objectForKey:@"URL"];
    NSDictionary *request = @{@"token": @"TOKEN",
                              @"fields": @"text,title,url",
                              @"url": articleURL};

    [DiffbotRequests addObject:request];
}

NSError *error;
NSData *DiffbotRequestsJSONData = [NSJSONSerialization dataWithJSONObject:DiffbotRequests options:kNilOptions error:&error];
NSString *DiffbotRequestsJSONString = [[NSString alloc] initWithData:DiffbotRequestsJSONData encoding:NSUTF8StringEncoding];

NSDictionary *parameters = @{@"token": @"TOKEN",
                             @"batch": DiffbotRequestsJSONString};

[[AFDiffbotClient sharedClient] getPath:@"batch" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"%@", error);
}];

And here's the response I get back:

Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in (200-299), got 400" UserInfo=0xc2ee4d0 {NSLocalizedRecoverySuggestion=, AFNetworkingOperationFailingURLRequestErrorKey= { URL:

And after that it's just all the URLs I submitted.

EDIT 2: Added an image of the API above.

EDIT 3: Current, unworking code:

[AFDiffbotClient sharedClient].operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;
NSMutableArray *DiffbotRequests = [[NSMutableArray alloc] init];

for (NSDictionary *URLAndID in URLsAndIDs) {
    NSString *articleURL = [URLAndID objectForKey:@"URL"];
    NSDictionary *request = @{@"token": @"TOKEN",
                              @"fields": @"text,title,url",
                              @"url": articleURL};

    [DiffbotRequests addObject:request];
}

NSError *error;
NSData *DiffbotRequestsJSONData = [NSJSONSerialization dataWithJSONObject:DiffbotRequests options:NSJSONWritingPrettyPrinted error:&error];
NSString *DiffbotRequestsJSONString = [[NSString alloc] initWithData:DiffbotRequestsJSONData encoding:NSUTF8StringEncoding];

NSDictionary *parameters = @{@"token": @"TOKEN",
                             @"batch": DiffbotRequestsJSONString};


[[AFDiffbotClient sharedClient] setParameterEncoding:AFJSONParameterEncoding];
[[AFDiffbotClient sharedClient] postPath:@"batch" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"%@", responseObject);

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"%@", error);
}];
Doug Smith
  • 27,683
  • 54
  • 189
  • 363
  • To produce an array of things in JSON, put the things in an NSArray and then use NSJSONSerialization to convert to a NSData object. But note that the things in the NSArray must be strings, numbers, dictionaries, or other arrays. **See json.org for the syntax that should result.** As to what the individual requests should look like, that's up to the folks on the other end. Probably an NSDictionary (which JSON calls an "object"). – Hot Licks Jan 13 '14 at 04:35
  • Doug, on the `dataWithJSONObject` call, check `error` afterwards. And NSLog `DiffbotRequestsJSONString`, and look it over to see if it looks right. – Hot Licks Jan 16 '14 at 20:56
  • You should be making a POST, not a GET. – SK9 Jan 23 '14 at 00:38
  • @SK9 I've attached my current code. – Doug Smith Jan 23 '14 at 03:44

4 Answers4

1

It's informal:

NSURLRequest *request = [[AFDiffbotClient sharedClient] requestWithMethod:@"GET" path:   [NSString stringWithFormat:@"article?token=MYTOKEN&fields=url,text,title&url=%@", URL] parameters:nil];

Better way:

 NSArray *paramters = @[@"token": @"MYTOKEN",
                        @"fields":  @"url,text,title",
                        @"url":@"aURL"
                        ]
     NSURLRequest *request = [[AFDiffbotClient sharedClient] requestWithMethod:@"GET" path:@"article" parameters:parameters];

The parameters will be URL-encoded into your URL finally seems as your origin one.

If you want POST JSONArray, you should use a POST method and there is a postObject. And also you should set a postObject encode method like:

typedef enum {
    AFFormURLParameterEncoding,
    AFJSONParameterEncoding,
    AFPropertyListParameterEncoding,
} AFHTTPClientParameterEncoding;
NULL
  • 313
  • 1
  • 12
aelam
  • 2,578
  • 2
  • 25
  • 31
  • Thanks for the parameter answer, but I'm still confused for supplying a JSON array of the requests. I understand to POST it, but I'm unsure of *what* exactly to post, as in what it should contain and what the data structure should be. – Doug Smith Jan 13 '14 at 02:48
  • you should research HTTP protocol http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol, many people don't know how the data send to server – aelam Jan 13 '14 at 03:18
  • You're misunderstanding me, I understand how data is sent to the server. *In the context of Objective-C I don't know what to submit and how to construct it.* – Doug Smith Jan 13 '14 at 14:38
  • So you don't know the server API? – aelam Jan 13 '14 at 16:13
  • In Objective-C? No, I guess not. I don't understand how I would go about creating a JSON array (would I create a `NSMutableArray` with `NSMutableDictionary`s? What would go in the dictionaries?) and sending it (how do I send it off to the API as a parameter?). – Doug Smith Jan 13 '14 at 21:28
  • convert NSArray to NSString with JSONKit or other JSON libs – aelam Jan 14 '14 at 04:04
  • The best way to convert NSArrays and NSDictionarys to JSON is with Apple's NSJSONSerialization class. You can use 3rd party kits, but then you have to include them in your app and you're going off in a different direction from most iOS app writers. – Hot Licks Jan 16 '14 at 20:51
1

For AFNetworking 2.0
Below code shows how you can pass dictionary of key value pairs to an URL

 AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

NSDictionary *parameters = @{@"token": @"TOKEN"}; // you can add as many parameters as you want it in this dictionary in your case

parameters = nil; // **to show this sample code works** i have set **parameters to nil**

// if you want to sent parameters you can use above code 

manager.requestSerializer = [AFJSONRequestSerializer serializer];

[manager POST:@"http://maps.google.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=false" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{

    NSLog(@"JSON: %@", responseObject);


  }failure:^(AFHTTPRequestOperation *operation, NSError *error)
 {
    NSLog(@"Error: %@", error);
 }];
bhavya kothari
  • 7,471
  • 4
  • 25
  • 53
0

Replace your current code with this options:NSJSONWritingPrettyPrinted:

NSData *DiffbotRequestsJSONData = [NSJSONSerialization dataWithJSONObject:DiffbotRequests options:NSJSONWritingPrettyPrinted error:&error];
NSString *DiffbotRequestsJSONString = [[NSString alloc] initWithData:DiffbotRequestsJSONData encoding:NSUTF8StringEncoding];

NSDictionary *parameters = @{@"token": @"TOKEN",
                             @"batch": DiffbotRequestsJSONString};

Edit:

The exact chunk of code you posted plus options:NSJSONWritingPrettyPrinted worked for me every time I needed to post to the server a JSON array. I would suggest creating a php file with the next code and send your request there. That way you'll know exactly what you are sending compared to what the server expects and of course, what you are missing.

<?php
set_time_limit(0);
mail("your@mail.com",basename(__FILE__).' - Recieved',var_export($HTTP_RAW_POST_DATA,true));
?>
Segev
  • 18,421
  • 12
  • 71
  • 148
  • Changing the options resulted in the same issue. – Doug Smith Jan 16 '14 at 14:51
  • Using Charles (as another user pointed out, it monitors outgoing traffic), I got this as my request for "JSON Text": gist.github.com/christianselig/a379f46d2826644075f2 and it shows this under "JSON" indicating that maybe I'm not posting JSON and just a string? i.imgur.com/1duXDIS.png – Doug Smith Jan 18 '14 at 21:03
  • Whether or not JSON is "pretty printed" should make no difference to a properly functioning server. Pretty printing simply makes the text longer and slower to transmit. – Hot Licks Jan 23 '14 at 00:25
0

Your code doesn't actually seem to be sending a POST, it looks like a GET. You need to use the AFNetworking POST method.

I can't see what the server is expecting because you need an account, but you should be checking what it actually sent against what the server expects. Use a tool like Charles to record what is sent so you can examine it.

When using the AFNetworking you need to set the serialisation type (you say it's JSON), the default will usually be form URL encoded. Then, the parameters dictionary that you pass will be converted into JSON.


From your screen shot of the API spec, this is wrong:

    NSDictionary *request = @{@"token": @"TOKEN",
                          @"fields": @"text,title,url", ...

Because you should be supplying the method and a URL (where the URL contains the token and such).

Your screenshot shows sample JSON, make your output in Charles look like that.

Wain
  • 117,132
  • 14
  • 131
  • 151
  • Using Charles, I got this as my request for "JSON Text": https://gist.github.com/christianselig/a379f46d2826644075f2 and it shows this under "JSON" indicating that maybe I'm not posting JSON and just a string? http://i.imgur.com/1duXDIS.png – Doug Smith Jan 18 '14 at 20:12
  • And here's what my code is at the moment, with my token removed: https://gist.github.com/christianselig/3ac65340b4c4bfb4f75f – Doug Smith Jan 18 '14 at 20:13
  • Looks like JSON. But what is the server expecting? – Wain Jan 18 '14 at 21:17
  • All I know is [what the API says](http://diffbot.com/dev/docs/batch/). It seems like JSON, no? – Doug Smith Jan 18 '14 at 21:49
  • Oh shoot! It's been brought to my attention that the link is behind a login wall. Here's an image of what I'm talking about: http://i.imgur.com/bGz0pgF.png – Doug Smith Jan 18 '14 at 22:09