3

I want to send imageUrl in POST parameter which can have &.e.g. imageUrl can be http://www.url.com?param1=edfef&param2=erfregreg.

Now If I send this directly to server then it will not consider any params after &. Other answers on SO suggest to replace & with %26. But that is not working. Because when I escape the whole POST param string then it will replace % in %26 with %25. i.e.http://www.url.com?param1=edfef%2526param2=erfregreg which makes url invalid.

Code to escape params :

NSMutableString *postParams = [NSMutableString stringWithFormat:@"imageUrl=%@&realName=%@, imageUrl, realName];
NSMutableString *escapeParams = (NSMutableString*)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,  (CFStringRef)postParams, NULL,(CFStringRef)@" !%?$#[]/+:@;*()'\"",kCFStringEncodingUTF8));   
self.postParameters = escapeParams;

Update : I tried below code, but not working.

// URL
NSString * url = [NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?width=250&height=250", fbId];

NSString *escapedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
    NULL,
   (__bridge CFStringRef) url,
    NULL,
    CFSTR("!*'();:@&=+$,/?%#[]\" "),
    kCFStringEncodingUTF8));

url = escapedString;

// Making REST API call to store user details in table.
NSMutableString *postParams = [NSMutableString stringWithFormat:@"imageUrl=%@&realName=%@", imageUrl, realName];
NSMutableString *escapeParams = (NSMutableString*)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,  (CFStringRef)postParams, NULL,(CFStringRef)@" !%?$#[]/+:@;*()'\"",kCFStringEncodingUTF8));
postParams = escapeParams;
NSString * getParams = ...;

// Code to call API.
.
.   // Setting up url and NSMutableURLRequest
.
NSData *myRequestData = [NSData dataWithBytes:[postParams UTF8String] length:[postParams length]];
[request setHTTPBody:myRequestData];
Geek
  • 8,280
  • 17
  • 73
  • 137

4 Answers4

1

Here is a copy-paste code snippet that I use in all my projects it encodes perfectly

- (NSString *)urlencode:(NSString *)val {
    NSMutableString *output = [NSMutableString string];
    const unsigned char *source = (const unsigned char *)[val UTF8String];
    int sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' '){
            [output appendString:@"+"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    return output;
}

I suggest you to use a NSString Categroy

Header

//#import "NSString+UrlEncode.h"
#import <Foundation/Foundation.h>

@interface NSString (UrlEncode)
- (NSString *)urlencode;
@end

Implementation

//#import "NSString+UrlEncode.m"
#import "NSString+UrlEncode.h"

@implementation NSString (UrlEncode)
- (NSString *)urlencode {
    NSMutableString *output = [NSMutableString string];
    const unsigned char *source = (const unsigned char *)[self UTF8String];
    int sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' '){
            [output appendString:@"+"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    return output;
}
@end

Usage: import the category

#import "NSString+UrlEncode.h"

then

  param.urlencode 
Durai Amuthan.H
  • 31,670
  • 10
  • 160
  • 241
  • That's "almost" correct. According w3c spec, these _unreserved_ characters "*", "-", ".", "0" to "9", "A" to "Z", "_", "a" to "z" shall not be escaped. – CouchDeveloper Mar 18 '14 at 08:09
  • The tilde character needs special considerations: according [RFC 3986](http://tools.ietf.org/html/rfc3986) which defines the rules for URIs, the tilde should not be created by URI producers. Thus it should be escaped as well. Consequently, when percent encoding a _URI_ parameter (and only an URI) we might want to escape the tilde as well. – CouchDeveloper Mar 18 '14 at 08:20
0

the = needs to be escaped (url encoded) as well.

See this answer on how to encode correctly ("!*'();:@&=+$,/?%#[]\" ").

Community
  • 1
  • 1
alex-i
  • 5,406
  • 2
  • 36
  • 56
  • I tried it. It converts `http://graph.facebook.com/1005465768888979/picture?width=250&height=250` to `http%3A%2F%2Fgraph.facebook.com%2F1005465768888979%2Fpicture%3Fwidth%3D250%26height%3D250`. Its not working. Giving nil image after downloading. I have changed one value because I cannot give correct url. – Geek Mar 16 '14 at 16:35
  • @Geek when I'm accessing http://graph.facebook.com/1005465768888979/picture?width=250&height=250 I get an error in json (OAuthException). Make sure you authenticate and send the authentication parameters to facebook. – alex-i Mar 16 '14 at 17:49
  • Dude, I specified in comment that I have modified value in url because it is Facebook profile image url so I cannot expose it. I have changed Facebook id value to make it invalid. I just wanted to show you escaped chars. If you know how to get your Facebook account id then you can use same url with your id to load in browser. – Geek Mar 16 '14 at 18:51
  • the encoded url looks just fine.. you're getting a nil image because the response is not a valid image. Have a look on the `NSData` response (convert it to a `NSString` and `NSLog` it or place a breakpoint) to have a clue on what's wrong. – alex-i Mar 17 '14 at 06:36
0

I used a workaround.

I escape the string using answer of alex-i and call API to save in database.

NSString *escapedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
    NULL,
   (__bridge CFStringRef) unescapedURL,
    NULL,
    CFSTR("!*'();:@&=+$,/?%#[]\" "),
    kCFStringEncodingUTF8));

Whenever I get it from database I replace %26 with & in url string.

Geek
  • 8,280
  • 17
  • 73
  • 137
-1

An easy solution for your problem that might work is not to use an application/x-www-urlformencoded content type, but instead to use JSON. That is, specify application/json as the value for the Content-Type header in your HTTP request and send the parameters as JSON:

So, first create a JSON from your parameters:

[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

NSDictionary* params = @{@"imageUrl": url, @"realName": name};

NSError* error;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];

Pass that JSON data as your request data.

Note:

Sending POST parameters as application/x-www-urlformencoded requires properly encoded parameters. How to precent encode the parameters is likely the most wrongly answered question on SO, though many incorrectly encoded parameters will work anyway on most servers.

The definitive answer how to encode correctly is here:

The algorithm as specified by w3c.

I've put an implementation of the above algorithm in Objective-C based on Core Foundation elsewhere on SO, including a convenient method which converts a NSDictionary to a NSData object, properly percent encoded.

However, given the above specification, it should be easy to implement it yourself - without using Core Foundation or Foundation, and thus becoming dependent on that Core Foundation implementation, which may break that algorithm when Core Foundation changes.

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • What happens when the problematic charcter is in the JSON data? This doesn't solve the problem. – Alexander Farber Mar 18 '14 at 10:41
  • Unlike `application/x-www-url-formencoded`, JSON does not require percent encoding. So, strictly, when there is a *valid* URI, which is properly percent encoded as specified by [RFC 3986](http://tools.ietf.org/html/rfc3986), this (ASCII) string can be put AS IS into the JSON. On the other hand, when using x-www-form-urlencoded content type this already properly encoded URI would need to be encoded again as specified by w3c. This "double percent encoding" with different encoding algorithm could possibly lead to issues. – CouchDeveloper Mar 18 '14 at 12:22