如何指定 Get-Request 编码(Retrofit + OkHttp)

Posted

技术标签:

【中文标题】如何指定 Get-Request 编码(Retrofit + OkHttp)【英文标题】:How to specify Get-Request encoding (Retrofit + OkHttp) 【发布时间】:2017-12-30 07:09:35 【问题描述】:

我在我的 android 应用程序中使用 Retrofit2 + OkHttp3 向 REST 服务器发出 GET - 请求。问题是服务器没有指定它提供的 JSON 的编码。这会导致接收到 'é' 作为 '�'(Unicode 替换字符)。

有没有办法告诉 Retrofit 或 OkHttp 响应的编码是什么?

这就是我初始化 Retrofit 的方式(Kotlin 代码):

val gson = GsonBuilder()
        .setDateFormat("d.M.yyyy")
        .create()

val client = OkHttpClient.Builder()
        .build()

val retrofit = Retrofit.Builder()
        .baseUrl(RestService.BASE_URL)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()

val rest = retrofit.create(RestService::class.java)

PS:服务器不是我的。所以我无法解决服务器端的初始问题。

编辑:最终解决方案

class EncodingInterceptor : Interceptor 

    override fun intercept(chain: Interceptor.Chain): Response 
        val response = chain.proceed(chain.request())
        val mediaType = MediaType.parse("application/json; charset=iso-8859-1")
        val modifiedBody = ResponseBody.create(mediaType, response.body().bytes())
        val modifiedResponse = response.newBuilder()
                .body(modifiedBody)
                .build()

        return modifiedResponse
    

【问题讨论】:

【参考方案1】:

一种方法是构建一个Interceptor,它接受响应并设置一个适当的Content-Type,如下所示:

class ResponseInterceptor : Interceptor 
    override fun intercept(chain: Interceptor.Chain): Response 
        val response = chain.proceed(chain.request())
        val modified = response.newBuilder()
                .addHeader("Content-Type", "application/json; charset=utf-8")
                .build()

        return modified
    

您可以像这样将它添加到您的 OkHttp 客户端:

val client = OkHttpClient.Builder()
        .addInterceptor(ResponseInterceptor())
        .build()

您应确保仅将此 OkHttpClient 用于未指定编码的 API,或者让拦截器仅添加适当端点的标头以避免覆盖来自其他端点的有效内容类型标头。

【讨论】:

哇,真快。现在我只需要找出他们使用的编码;)谢谢。 OkHttp 似乎忽略了我设置的字符集。即使在添加正确的之前删除旧的 Content-Type: application/json 。但是,我可以通过在拦截器中使用 InputStreamReader(response.body().byteStream(), "ISO-8859-1").readText() 来手动获取正确的字符串。但我无法将其添加到修改后的响应中。 我的第三条也是最后一条评论;)我得到了它的工作并将解决方案添加到我的问题中。感谢您向我展示拦截器的工作原理。 很高兴你能成功!我只是看着它,意识到您需要重新创建 ResponseBody,但您首先找到了它!【参考方案2】:
class FixEncodingInterceptor implements Interceptor 

    @Override
    public Response intercept(Chain chain) throws IOException 
        Response response = chain.proceed(chain.request());
        MediaType oldMediaType = MediaType.parse(response.header("Content-Type"));
        // update only charset in mediatype
        MediaType newMediaType = MediaType.parse(oldMediaType.type()+"/"+oldMediaType.subtype()+"; charset=windows-1250");
        // update body
        ResponseBody newResponseBody = ResponseBody.create(newMediaType, response.body().bytes());

        return response.newBuilder()
                .removeHeader("Content-Type")
                .addHeader("Content-Type", newMediaType.toString())
                .body(newResponseBody)
                .build();
    

并添加到 OkHttp:

builder.addInterceptor(new FixEncodingInterceptor());

【讨论】:

【参考方案3】:

这篇文章很旧,但我在 Kotlin 中找到了一个适合我的解决方案(@BryanHerbst 的答案对我不太适用)

class EncodingInterceptor : Interceptor 
override fun intercept(chain: Interceptor.Chain): Response 
    val response = chain.proceed(chain.request())
    var encodedBody = ""
    val encoding = InputStreamReader(
        response.body?.byteStream(),
        Charset.forName("ISO-8859-1")
    ).forEachLine 
        encodedBody += it
    

    return response.newBuilder()
        .addHeader("Content-Type", "application/xml; charset=utf-8")
        .body(encodedBody.toResponseBody())
        .build()
    

【讨论】:

以上是关于如何指定 Get-Request 编码(Retrofit + OkHttp)的主要内容,如果未能解决你的问题,请参考以下文章

air jordan 13 retro 2017 reviews and story

javascript bootstrap_retro_prepworx.js

Air Jordan 8 Retro Performance Review

在iPhone中使用Retro Avatar API

JSON REST Get-Request 正在等待用于 Chrome/Firefox 的 Tomcat 上的 CORS

在应用程序中初始化Retrofit客户端