2


I don't know if this is a Node.js or an iOS problem, but when I try to send a JSON object which contains umlauts (ä, ö, and ü) the set content-length seems to be wrong.

So here's my setup:

My Node.js server sends data via:

[..mongodb request..].toArray(function(err, result) {
    res.setHeader('Content-Type', 'application/json');
    var body = JSON.stringify(result);

    console.log(body);
    console.log(body.length);

    res.setHeader('charset', 'utf8');
    res.setHeader('Content-Length', body.length);
    res.end(body);
});

This yields the following object:

[
{
    "_id": "51da7eb5d5f9ad77302a26c6",
    "loc": [
        53.560994,
        9.929796
    ],
    "street": "Kühnehöfe 25",
    "time": 1373273781535
},
{
    "_id": "51da7eb9d5f9ad77302a26c7",
    "loc": [
        53.561899,
        9.930203
    ],
    "street": "Kühnehöfe 17",
    "time": 1373273785156
}
]

Which has (parsed as string) a length of 215. This is also set as the content length.

In my iOS project I've got following setup:

-(void)serverRequest {

    // Initialize new mutable data
    NSMutableData *data = [[NSMutableData alloc] init];
    self.receivedData = data;

    // Initialize URL that is going to be fetched.
    NSURL *url = [NSURL URLWithString:@"http://localhost:8000/getSpots"];

    // Initialize a request from a URL
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[url standardizedURL]];

    // Set HTTP method
    [request setHTTPMethod:@"POST"];

    // Initialize post data
    NSDictionary* jsonDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:coordinate.latitude], @"lat", [NSNumber numberWithFloat:coordinate.longitude], @"lng", accessToken, @"accessToken", nil];//dictionaryWithObjectsAndKeys:coord.latitude, nil]
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict
                                                   options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
                                                     error:&error];

    // Set request content type we MUST set this value.
    [request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"content-type"];

    // Set post data of request
    [request setHTTPBody:jsonData];

    // Initialize a connection from request
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    self.connectionGetSpots = connection;

    // Start the connection
    [connection start];
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    [self.receivedData appendData:data];

    NSLog(@"loading: %d, %lld", [self.receivedData length], dataSize);  // Both 215 (correct)
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    dataSize = [response expectedContentLength];
    NSLog(@"dataSize: %lld", dataSize);  // is 215 (correct)
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSString *responseData = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
    NSLog(@"rD: %@, %d" ,responseData, [responseData length]);
}

The connectionDidFinishLoading function logs this:

[
{
    "_id": "51da7eb5d5f9ad77302a26c6",
    "loc": [
        53.560994,
        9.929796
    ],
    "street": "Kühnehöfe 25",
    "time": 1373273781535
},
{
    "_id": "51da7eb9d5f9ad77302a26c7",
    "loc": [
        53.561899,
        9.930203
    ],
    "street": "Kühnehöfe 17",
    "time": 13732737851
,211

As you can see, there are four umlauts in the JSON object and four characters are missing. If I add another location with two umlauts, two more characters will be missing.

I guess somewhere the content type is set wrong, but I'm not sure what I have to do.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Alexander Scholz
  • 2,100
  • 1
  • 20
  • 35
  • The JSON string in your question has 315 characters (not 215). Since every umlaut is encoded as 2 UTF-8 bytes, the content length should be 319. – Martin R Jul 08 '13 at 09:36
  • sorry the original string i send is: `[{"_id":"51da7eb5d5f9ad77302a26c6","loc":[53.560994,9.929796],"street":"Kühnehöfe 25","time":1373273781535},{"_id":"51da7eb9d5f9ad77302a26c7","loc":[53.561899,9.930203],"street":"Kühnehöfe 17","time":1373273785156}]` I formatted it for better readability – Alexander Scholz Jul 08 '13 at 09:39
  • Then, your string has 231 bytes. What does your JS statement `body.length` return? The number of *characters* or the number of bytes (unlikely)? It would probably be better to ensure you are setting an array of bytes, not a string, in your request. – CouchDeveloper Jul 08 '13 at 10:28
  • Yes, it returns the number of characters. – Alexander Scholz Jul 08 '13 at 10:52
  • It works if I use `encodeURI(body).split(/%..|./).length - 1` instead of `body.length`. Thanks for your help – Alexander Scholz Jul 08 '13 at 10:57
  • I'm no expert in JS, but that sounds ineffective. Maybe there is a better solution. ;) – CouchDeveloper Jul 08 '13 at 12:20

1 Answers1

4

Use this for correct UTF-8 lengths:

new Buffer(body).length
joewhite86
  • 428
  • 3
  • 6
  • Nice! And the encoding is per default UTF-8 ;) I'm not the OP, but wouldn't it be better to set the Buffer instance as the body of the request? Is a Content-Length header *explicitly* required, too? – CouchDeveloper Jul 08 '13 at 12:35
  • Thanks. You would have to convert the Buffer back to String (toString('utf-8')), so there's no difference. And yes, from my experience a "Content-Length" header is indeed required. – joewhite86 Jul 08 '13 at 14:29