OkHttp 和 UTF-8 字符编码

Mat*_*son 5 java android utf-8 character-encoding okhttp

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

我们正在构建的应用程序使用 OkHttp 对我们的服务器系统进行 GET 和 POST 调用。服务器在 Apache 后面的 Tomcat 上运行。默认情况下,Apache 和 Tomcat 都配置为使用 UTF-8 字符编码。我假设需要的是从 Android 应用程序发送到服务器的 http 请求都配备了一个包含“application/text; charset=utf-8”之类的标头。

我构建了这个精简的代码示例来说明这个问题。如您所见,我在设置标头的请求中包含了 addHeader()。我还在 RequestBody 上主动设置了一个字符集。

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";
}
Run Code Online (Sandbox Code Playgroud)

在服务器端,我记录了名为 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
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:我们这样做是否错误?如果是,那么正确的方法是什么?最坏的情况,这可能是 OkHttp 中的一个错误,但我对此表示怀疑。

为了比较,我构建了一个简单的 html 表单来制作完全相同的帖子,并且以这种方式发送的相同字符串作为“xxåäöÅÄÖxx”出现,这是正确的。

Gro*_*uez 3

这里至少存在两个不同的问题。

\n\n

1.您的 Content-type 标头被(正确地)忽略

\n\n

当您稍后调用对象时,您设置的 Content-type 标头将被.post(requestBody)覆盖request。这是因为您正在使用一个FormBuilder对象来构建 POST 正文,并且该对象专门用于表单application/x-www-form-urlencoded。如果你想发布 JSON 数据,你不应该使用它。相反,试试这个:

\n\n
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");\nOkHttpClient client = new OkHttpClient();\n\nString post(String url, String json) throws IOException {\n  RequestBody body = RequestBody.create(JSON, json);\n  Request request = new Request.Builder()\n      .url(url)\n      .post(body)\n      .build();\n[...]\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是官方 OkHttp 示例的完整源代码。

\n\n

2.非ASCII字符乱码

\n\n

即使您坚持使用application/x-www-form-urlencoded内容类型,非 ASCII 文本也应该可以正常工作。那么你的情况是怎么回事?

\n\n

我怀疑你编译源代码时存在编码问题;即 javac 使用的字符集与 Java 源文件的字符集不匹配。您可能希望显式地将-encoding utf8(或您在源文件中使用的任何编码)传递给 javac,或者更好的是,避免源代码中出现任何非 ASCII 字符并使用 Unicode 转义符。在这种情况下,xx\xc3\xa5\xc3\xa4\xc3\xb6\xc3\x85\xc3\x84\xc3\x96xx您可能想要使用而不是xx\\u00E5\\u00E4\\u00F6\\u00C5\\u00C4\\u00D6xx

\n