Encoding: UTF-8 für HttpClient und POST requests

23. Februar 2009


 

Eigentlich ist ja eines der angenehmen Features von Java, dass Strings immer Unicode sind, und man keine Encoding-probleme hat … jedenfalls so lange nicht, bis man mit der Aussenwelt in Kontakt tritt.
Dann aber gab es immer die Frage, welche der 8bit Stringdarstellungen denn nun „die Richtige“ ist, oder auch erstmal, wo bitte die vielen „?“ im Text herkommen. Seit sich UTF-8 praktisch durchgesetzt hat, stellt sich diese Frage anders: „Wo ist bei der Schnittstelle die Konfigurationsoption, in der ich ‚UTF-8‘ eintragen muss?“

Im Kontext von Web-servern und -applicationen gibt es da schon genügend Beispiele, wo überall konfiguriert werden muss, damit sowohl Request als auch Response in UTF-8 über die Leitung gehen.

Neulich ist mir das Problem bei einen anderen Kandiaten über den Weg gelaufen, wo ich die Konfiguration noch nicht kannte.

Der Kandidat ist der apache-commons HttpClient (genauer gesagt die org.apache.commons.httpclient.methods.PostMethod klasse in der Library).
Mit HttpClient lassen sich bekanntlich eben mal schnell Verbindungen zu anderen Webservern aufbauen und Inhalte abholen – das Java-Äquivalent zu wget und/oder curl in Shell scripten.

Muss man die Daten via POST verschicken, sieht das in etwa so aus:


HttpClient client = new HttpClient();
PostMethod postMethod = new PostMethod( someUrl );
postMethod.getParams().setContentCharset("utf-8"); // (!) hier wird das encoding gesetzt
postMethod.addParameter("data", someNonAsciiData );
int returnCode = client.executeMethod( billingMethod );
String responseStr
= request.getResponseBodyAsString(); // wenn man sicher ist, text zurueckzubekommen

Damit die Daten korrekt UTF-8 codiert (bzw in die entsprechenden URL-encodierte Form gebracht wird), muss dabei in der „params“ Property der contentCharset korrekt gesetzt werden – sonst nimmt HttpClient den „historischen“ Default-wert „ISO-8859-1“, und nicht UTF-8.

Bei GET-Requests besteht das Problem nicht – da hält sich HttpClient an die Empfehlung der RFC 3986, UTF-8 zu benutzen, solange nichts anderes angegeben ist. (Wer hier unbedingt etwas anderes haben will, weil zum Beispiel mit einem altersstarrsinnigen Server kommuniziert werden soll, kann das URL Encoding ändern, z.B. mit method.getParams().setUriCharset("iso-8859-1") ).

Getestet mit der (alten) commns-httpclient-3.1.jar … mit Version 4.0, die gerade in beta-stadium ist, ist es wohl wieder anders geworden. Das probiere ich aber ein anderes mal aus, wenn das entweder stabil ist, oder ich Zeit für Experimente habe …

Nachtrag: Wer aus irgendwelchen Gründen mit httpclient-2.x arbeitet (zum Beispiel, weil maven einem diese Version über irgendwelchen Abhängigkeiten unterjubelt), muss dem HttpClient das Encoding statt dessen über folgenden VooDoo Tanz unterjubeln:


postMethod.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")

Da ist in etwa äquivalent zu Servlets, bei denen das Encoding in der Response auch über den Content-Type gesetzt wird, z.b. durch den Aufruf: response.setContentType("text/html; charset=UTF-8"); … nur das beim HttpClient der Teil vor dem charset etwas länger ist. Der „charset=utf-8“ wird das vom Request aus dem „Content-Type“ Header manuell herausgeparsed.