OkHttp和UTF-8字符编码

7

我有一个关于Android中OkHttp及其支持字符编码的问题,特别是如何使用UTF-8来支持瑞典字符 å、ä 和 ö(以及大写字母ÅÄÖ)。

我们正在构建的应用程序使用OkHttp向服务器系统发出GET和POST调用。服务器在Apache后面运行Tomcat。 Apache和Tomcat都默认配置为使用UTF-8字符编码。我认为所需的是从Android应用程序发送到服务器的http请求需要装备一个头部包含类似于“application/text; charset=utf-8”的内容。

我编写了这个简化的代码示例来说明这个问题。正如您所看到的,我已经在请求中添加了addHeader()来设置标题。 我还在RequestBody上积极设置了Charset。

public static String testPost() throws IOException{
    OkHttpClient okHttpClient = new OkHttpClient();
    HttpUrl.Builder builder = new HttpUrl.Builder();
    HttpUrl httpUrl = builder.scheme("https")
                             .host("dev.ourdomainname.com")
                             .addPathSegment("characterencoding")
                             .build();
    Charset charset = Charset.forName(StandardCharsets.UTF_8.name());
    RequestBody requestBody = new FormBody.Builder(charset)
                                          .add("text", "xxåäöÅÄÖxx")
                                          .build();
    Request request = new Request.Builder()
            .url(httpUrl)
            .addHeader("Content-Type", "application/json; charset=utf-8")
            .post(requestBody)
            .build();
    Response response = okHttpClient.newCall(request).execute();
    return "test completed";
}

在服务器端,我记录了名为text的参数的值,它以"xxåäöÃ?Ã?Ã?xx"的形式传入,显然这是不够好的。我还有一些代码,可以循环遍历请求中的所有头,并将它们记录下来。输出结果如下所示。请注意,没有"application/text; charset=utf-8"头。

DEBUG 23 Jan 14:52:37.128 - testCharacterEncoding. text: xxåäö���xx
DEBUG 23 Jan 14:52:37.129 - Header: content-type with value: application/x-www-form-urlencoded
DEBUG 23 Jan 14:52:37.129 - Header: content-length with value: 45
DEBUG 23 Jan 14:52:37.129 - Header: host with value: dev.cqrify.com
DEBUG 23 Jan 14:52:37.129 - Header: connection with value: Keep-Alive
DEBUG 23 Jan 14:52:37.129 - Header: accept-encoding with value: gzip
DEBUG 23 Jan 14:52:37.129 - Header: user-agent with value: okhttp/3.9.1

所以我的问题是:我们这样做错了吗?如果是的话,正确的方式是什么?最坏的情况是,这可能是OkHttp中的一个错误,但我怀疑。

为了比较,我建立了一个简单的HTML表单来进行完全相同的发布,通过这种方式发送的相同字符串为“xxåäöÅÄÖxx”,这是正确的。


2
.addHeader("Content-Type", "application/json; charset=utf-8")Header: content-type with value: application/x-www-form-urlencoded不匹配。请详细说明。 - greenapps
嗯,greenapps,那似乎是问题的中心。请求使用的Content-type与我添加的不同。 - Mats Andersson
1
@greenapps,我已经点赞了你的评论,但后来意识到Mats所展示的内容——日志条目是他尝试使用简单的HTML页面进行表单提交时生成的。Mats - 你的Java代码,例如.add("text", "xxåäöÅÄÖxx")是否没有被正确编译,可能是因为编码不受支持?你可以尝试使用System.out.println打印一下,看看它是否能够正常输出——也许这个GitHub 示例可以帮到你? - JGlass
不,日志记录代码是从接收服务器代码中调用的,当应用程序发起调用时。 - Mats Andersson
1
我尝试了你的代码,对我来说它可以正常工作。我只是让一个 PHP 脚本返回文本,它也能正常返回。使用 okhttp-3.4.1。省略字符集,因为 FormBody 构造函数没有该参数。 - greenapps
显示剩余6条评论
1个回答

3
这里至少有两个不同的问题。
1. 你设置的Content-Type头被正确地忽略了
当你稍后在request对象上调用.post(requestBody)时,你设置的Content-Type头被覆盖了。这是因为你使用了一个FormBuilder对象来构建POST请求体,而这个对象专门用于application/x-www-form-urlencoded表单。如果你想要发布JSON数据,就不应该使用它。相反,可以试试下面的方法:
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
[...]

这是官方OkHttp示例的完整源代码,请查看
2. 非ASCII字符被破坏
即使您坚持使用“application/x-www-form-urlencoded”内容类型,非ASCII文本也应该正常工作。那么在您的情况下发生了什么呢?
我怀疑在编译源代码时出现了编码问题;即javac使用的字符集与您的Java源文件的字符集不匹配。您可能需要显式传递“-encoding utf8”(或您在源文件中使用的任何编码方式)给javac,或者更好的方法是避免在源代码中使用任何非ASCII字符,而改用Unicode转义。在这种情况下,您可以使用“xx\u00E5\u00E4\u00F6\u00C5\u00C4\u00D6xx”代替“xxåäöÅÄÖxx”。

你好,Grodriguez,感谢您的回复。我发帖已经有一段时间了,我意识到应该关闭它并分享解决方案。 - Mats Andersson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接