doubutao6216 2016-09-29 19:45
浏览 230

iOS - Objective C - POST字符串转义/编码问题

After about a week of research with no solution, I give up and I ask you guys for suggestions.

I need the same php script to handle POST requests in the same way, regardless of their origin which is either a simple HTML form, or an iOS app. No JSON involved.

Issue: Some chars are lost when the POST sender is the iOS app. At this point I would say that I don't know why it behaves like that. Apparently the web browser escapes all the chars correctly in the build up of the POST request, while my obj-c code does not.

In the objective-c code you can see 5 of the tentatives I did, with a little note of what went wrong.

I really hope you can help

tester.php file:

<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_POST['email']))
{
    echo "email: " . $_POST['email'];
    echo "
";
    echo "password: " . $_POST['password'];
}
else
{
?>

<form method="post">
    <input type="text" id="email" name="email" value="切换到中文@gmail.com" />
    <br>
    <input type="text" id="password" name="password" value="&+=/切/().%&" />
    <br>
    <input type="submit" />

</form>

<?php
}

Objectice-c HTTP POST execution:

// Get values
NSString *email    = @"切换到中文@gmail.com";
NSString *password = @"&+=/切/().%&";

NSLog(@"Before: %@ and %@", email, password);

// Escape tentative 1: fails: % disappears
//email = [email stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];
//password = [password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];

// Escape tentative 2: failed: % disappears, + becomes a space, who knows what else
// email = [email stringByReplacingOccurrencesOfString: @"&" withString: @"%26"];
// password = [password stringByReplacingOccurrencesOfString: @"&" withString: @"%26"];

// Escape tentative 3: % disappears
// NSString *charactersToEscape = @"!*'();:@&=+$,/?%#[]\" ";
// NSCharacterSet *allowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:charactersToEscape] invertedSet];
// email = [email stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
// password = [password stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];

// Escape tentative 4: % disappears
/*CFStringRef cf_email = (__bridge CFStringRef)(email);
CFStringRef cf_password = (__bridge CFStringRef)(password);

email = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                  cf_email,
                                                                  NULL,
                                                                  CFSTR("%:/?#[]@!$&'()*+,;="),
                                                                  kCFStringEncodingUTF8));
password = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                  cf_password,
                                                                  NULL,
                                                                  CFSTR("%:/?#[]@!$&'()*+,;="),
                                                                  kCFStringEncodingUTF8));*/

// Escape tentative 5: password becomes "(null)"
/*email = [email stringByReplacingOccurrencesOfString: @"%" withString: @"%25"];
email = [email stringByReplacingOccurrencesOfString: @"&" withString: @"%26"];
email = [email stringByReplacingOccurrencesOfString: @"+" withString: @"%2b"];


password = [password stringByReplacingOccurrencesOfString: @"%" withString: @"%25"];
password = [password stringByReplacingOccurrencesOfString: @"&" withString: @"%26"];
password = [password stringByReplacingOccurrencesOfString: @"+" withString: @"%2b"];*/



// Execute HTTP request
NSString *postData = [NSString stringWithFormat:@"email=%@&password=%@", email, password];

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

NSURL *url = [NSURL URLWithString:@"http://localhost:8080/tester.php"];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

request.HTTPBody = [postData dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = @"POST";

NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // Print response
    NSLog([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[postDataTask resume];

Result if I am using the HTML form (exactly what I have inputted in the form):

email: 切换到中文@gmail.com password: &+=/切/().%&

What I get from the objective c code (some chars disappear), one of the last tests:

2016-09-29 21:28:59.519 test[44752:2438179] Before: 切换到中文@gmail.com (15) and &+=/切/().%& (11)
2016-09-29 21:28:59.705 test[44752:2438721] email: 切换到中文@gmail.com password: &+=/切/().&
  • 写回答

1条回答 默认 最新

  • dongmao9217 2016-10-03 20:10
    关注

    This is the way I do. It worked pretty well with everything I tried. Firstly, you need to add that function to NSString:

    @implementation NSString (NSStringWebStructure)
    -(NSString*)stringToWebStructure
    {
        NSString* webString = [self stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
        webString = [webString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
        webString = [webString stringByReplacingOccurrencesOfString:@"?" withString:@"%3F"];
    
        return webString;
    }
    @end
    

    Now you need the request functions:

    -(NSString*)launchURL:(NSURL*)url withPostString:(NSString*)post
    {
        NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
        NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[postData length]];
    
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
        [request setURL:url];
        [request setHTTPMethod:@"POST"];
        [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
        [request setHTTPBody:postData];
    
        NSError *error = nil;
        NSHTTPURLResponse *response = nil;
        NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    
        if (response.statusCode >= 200 && response.statusCode < 300)
        {
            NSString *responseData = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
            if (responseData.length > 0) return responseData;
        }
        else if (error) return [error description];
    
        return nil;
    }
    -(NSString*)launchURL:(NSURL*)url withPostValues:(NSDictionary*)postDict
    {
        NSArray* postKeys = postDict.allKeys;
        NSMutableArray* postLines = [[NSMutableArray alloc] init];
    
        for (NSString* key in postKeys)
        {
            [postLines addObject:[NSString stringWithFormat:@"%@=%@",key,[postDict[key] stringToWebStructure]]];
        }
    
        return [self launchURL:url withPostString:[postLines componentsJoinedByString:@"&"]];
    }
    

    And considering your example, you would just need to do that:

    NSString *email    = @"切换到中文@gmail.com";
    NSString *password = @"&+=/切/().%&";
    
    NSURL *url = [NSURL URLWithString:@"http://localhost:8080/tester.php"];
    NSDictionary* postValues = @{@"email":email, @"password": password};
    
    NSString* response = [self launchURL:url withPostValues: postValues];
    NSLog(@"%@",response);
    

    stringByAddingPercentEscapesUsingEncoding: was deprecated in macOS 10.11, but if you are going to support old macOS versions, like I do, this is a functional method (tested from macOS 10.6 to macOS 10.12).

    评论

报告相同问题?

悬赏问题

  • ¥15 BP神经网络控制倒立摆
  • ¥20 要这个数学建模编程的代码 并且能完整允许出来结果 完整的过程和数据的结果
  • ¥15 html5+css和javascript有人可以帮吗?图片要怎么插入代码里面啊
  • ¥30 Unity接入微信SDK 无法开启摄像头
  • ¥20 有偿 写代码 要用特定的软件anaconda 里的jvpyter 用python3写
  • ¥20 cad图纸,chx-3六轴码垛机器人
  • ¥15 移动摄像头专网需要解vlan
  • ¥20 access多表提取相同字段数据并合并
  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算