适用于iOS中HTTP POST请求的JSON编码

时间:2014-04-05 17:56:00

标签: ios json

之前发布了一条关于JSON解析无法正常工作的查询。使用数据包嗅探器和另一个正常工作的客户端进行了更多调查,发现它是一种语法问题,我似乎无法解决这个问题。

底部的代码使HTTP请求中包含JSON: {" key":" value"}

我的服务器实际上是在寻找以下语法的JSON: key =%22value%22

我尝试编写一些手动执行此操作的代码,但我认为iOS必须有一些开箱即用的功能,而且我不希望将来出现故障。

我试图找到合适的代码来解决这个问题,但是不能(你可以看到我试过注释掉的一些代码)。谁能帮我?

+ (NSString*)makePostCall:(NSString*)urlSuffix
                       keys:(NSArray*)keys
                    objects:(NSArray*)objects{
    NSDictionary *params = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
    // NSString *dataString = [self getDataStringFromDictionary:params];
    // NSData *jsonData = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params
                                                   options:0
                                                     error:&error];

    // id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
    // NSLog(@"%@", jsonObject);

    if (!jsonData) {
        // should not happen
        NSError *error;
        NSLog(@"Got an error parsing the parameters: %@", error);

        return nil;
    } else {
        // NSString *jsonRequest = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
        // NSLog(@"%@", jsonRequest);

        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", urlPrefix, urlSuffix]];

        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];


        // NSData *requestData = [jsonRequest dataUsingEncoding:NSUTF8StringEncoding];


        [request setHTTPMethod:@"POST"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
        // [request setValue:@"application/x-www-form-urlencoded;charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
        [request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [request setHTTPBody: jsonData];

        NSURLResponse * response = nil;
        NSError * error = nil;

        NSData * data = [NSURLConnection sendSynchronousRequest:request
                                          returningResponse:&response
                                                      error:&error];

        // TODO: handle error somehow

        NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

        return returnString;
    }
}

1 个答案:

答案 0 :(得分:1)

你说:

  

发生的事情是服务器期望的每个参数的内容实际上是JSON,但是每个参数都是单独发送的

如果您说您正在发布标准application/x-www-form-urlencoded,但想要对值进行JSON编码,这有点不寻常(我要么专门使用JSON或x-www-form-urlencoded,而不是结合它们),但这并不难。如您的示例所示,关键问题是您必须百分比转义生成的JSON。例如:

NSURL *url = [NSURL URLWithString:kURLString];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];

// information to be encoded for first parameter

NSDictionary *value1 = @{@"name" : @"Dwayne Johnson",
                         @"age"  : @41,
                         @"sex"  : @"male"};
NSError *error;
NSData *json1 = [NSJSONSerialization dataWithJSONObject:value1 options:0 error:&error];
NSAssert(json1, @"Unable to create JSON for value1: %@", error);

// information  to be encoded for second parameter

NSDictionary *value2 = @{@"name" : @"Lauren Hashian",
                         @"age"  : @29,
                         @"sex"  : @"female"};

NSData *json2 = [NSJSONSerialization dataWithJSONObject:value2 options:0 error:&error];
NSAssert(json2, @"Unable to create JSON for value1: %@", error);

// combine these parameters into single dictionary

NSDictionary *parameters = @{@"person" : json1, @"partner" : json2};
NSData *requestData = [self encodePostParameters:parameters];

[request setHTTPBody:requestData];

[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    if (!data) {
        NSLog(@"sendAsynchronousRequest failed: %@", connectionError);
        return;
    }

    NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

    // you can now use that `responseString` variable (but do this here, 
    // inside this completion block!)
}];

这将导致请求HTTPBody

person=%7B%22age%22%3A41%2C%22name%22%3A%22Dwayne+Johnson%22%2C%22sex%22%3A%22male%22%7D&partner=%7B%22age%22%3A29%2C%22name%22%3A%22Lauren+Hashian%22%2C%22sex%22%3A%22female%22%7D

这里有趣的逻辑是转义百分比,我在名为percentEscapeString的方法中执行,我从encodePostParameters调用,其定义如下:

/** Percent escape the post parameters for x-www-form-urlencoded request
 *
 * @param    parameters     A NSDictionary of the parameters to be encoded
 *
 * @return                  The NSData to be used in body of POST request (or URL or GET request)
 */
- (NSData *)encodePostParameters:(NSDictionary *)parameters
{
    NSMutableArray *results = [NSMutableArray array];

    for (NSString *key in parameters) {
        NSString *string;
        id value = parameters[key];
        if ([value isKindOfClass:[NSData class]]) {
            string = [[NSString alloc] initWithData:value encoding:NSUTF8StringEncoding];
        } else if ([value isKindOfClass:[NSString class]]) {
            string = value;
        } else {                         // if you want to handle other data types, add that here
            string = [value description];    
        }
        [results addObject:[NSString stringWithFormat:@"%@=%@", key, [self percentEscapeString:string]]];
    }

    NSString *postString = [results componentsJoinedByString:@"&"];

    return [postString dataUsingEncoding:NSUTF8StringEncoding];
}

/** Percent escape string
 *
 * Note, this does not use `stringByAddingPercentEscapesUsingEncoding`, because
 * that that will not percent escape some critical reserved characters (notably
 * `&` and `+`). This uses `CFURLCreateStringByAddingPercentEscapes`, instead.
 */
- (NSString *)percentEscapeString:(NSString *)string
{
    NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                                 (CFStringRef)string,
                                                                                 (CFStringRef)@" ",
                                                                                 (CFStringRef)@":/?@!$&'()*+,;=",
                                                                                 kCFStringEncodingUTF8));
    return [result stringByReplacingOccurrencesOfString:@" " withString:@"+"];
}