java 如何在okhttp中抑制Charset自动添加到Content-Type

lvjbypge  于 2023-05-27  发布在  Java
关注(0)|答案(3)|浏览(280)

请考虑以下代码:

OkHttpClient client = new OkHttpClient();

    MediaType mediaType = MediaType.parse("text/plain; charset=utf-8"); // [A]
    RequestBody body = RequestBody.create(mediaType, media);
    String[] aclHeader = "x-goog-acl:public-read".split(":");

    Request request = new Request.Builder()
            .addHeader("Content-Type", "text/plain") // [B]
            .addHeader(aclHeader[0], aclHeader[1])
            .url(url)
            .put(body)
            .build();

    Response response = client.newCall(request).execute();

我从客户端使用先前签名的URL访问GCS。
问题:似乎okhttp也将为body [A]声明的字符集添加到URL中(至少对于text/plain),即使它没有在[B]中声明。这打乱了我的签名URL,GCS返回403 Forbidden。

  • 如果我从[A]中删除字符集,它仍然是添加的。
  • 如果我在签名之前将字符集添加到签名的URL中,它会工作,GCS返回200 OK。

但这并不是它应该的样子。至少在处理签名URL时,这些URL必须完全按照声明发送到服务器。
我尝试使用Apache http客户端(我不想在生产中使用,因为okhttpclient已经是我安装的一部分),该客户端不会暴露此行为:

String[] aclHeader = "x-goog-acl:public-read".split(":");

        StatusLine statusLine = Request

                .Put(url)
                .addHeader("Content-Type", "text/plain")
                .addHeader(aclHeader[0], aclHeader[1])
                .bodyByteArray(media)

                .execute().returnResponse().getStatusLine();

有没有一种方法可以抑制okhttp中的行为,即它添加到Content-Type或在body中冗余地传输Content-Type?

bd1hkmkf

bd1hkmkf1#

我找到了解决方案:
下面这一行是罪魁祸首:

RequestBody body = RequestBody.create(mediaType, media);

create有3个介质签名:

  • 字符串
  • byte[]
  • 文件

当我传递一个String时,它会忽略提供的mediaType并向其添加字符集。即使对于图像/JPEG,它也会发送
image/jpeg; charset=utf-8
到服务器。
使用byte[]或File会抑制该行为。
我希望这对你有帮助!
[愚蠢的我-为了简单起见,我在测试时给了它一个字符串,因为我不关心身体;- ()

f4t66c6m

f4t66c6m2#

创建requestbody时,只需将内容类型设置为“null”,并添加header manual,如下所示:

OkHttpClient client = new OkHttpClient();

    RequestBody body = RequestBody.create('your media string', null);
    String[] aclHeader = "x-goog-acl:public-read".split(":");

    Request request = new Request.Builder()
            .addHeader("Content-Type", "text/plain") // [B]
            .addHeader(aclHeader[0], aclHeader[1])
            .url(url)
            .put(body)
            .build();

    Response response = client.newCall(request).execute();

因为当我阅读okhttp源代码时,在RequestBody.kt中,我发现以下代码:

/**
     * Returns a new request body that transmits this string. If [contentType] is non-null and lacks
     * a charset, this will use UTF-8.
     */
    @JvmStatic
    @JvmName("create")
    fun String.toRequestBody(contentType: MediaType? = null): RequestBody {
      var charset: Charset = UTF_8
      var finalContentType: MediaType? = contentType
      if (contentType != null) {
        val resolvedCharset = contentType.charset()
        if (resolvedCharset == null) {
          charset = UTF_8
          finalContentType = "$contentType; charset=utf-8".toMediaTypeOrNull()
        } else {
          charset = resolvedCharset
        }
      }
      val bytes = toByteArray(charset)
      return bytes.toRequestBody(finalContentType, 0, bytes.size)
    }
wlwcrazw

wlwcrazw3#

您可以注册一个networkInterceptor来覆盖Content-Type头,例如:FixContentTypeInterceptor.java:

import okhttp3.*;
import java.io.IOException;

public final class FixContentTypeInterceptor implements Interceptor {
    @Override public Response intercept(Interceptor.Chain chain) throws IOException {
        Request originalRequest = chain.request();

        Request fixedRequest = originalRequest.newBuilder()
                .header("Content-Type", "application/json")
                .build();
        return chain.proceed(fixedRequest);
    }

}

Main.java:

[...]
MediaType JSON = MediaType.get("application/json; charset=utf-8");
String json = "{}";
OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new FixContentTypeInterceptor())
                .build();
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
[...]

在okhttp的Github中有关于这一点的讨论。https://github.com/square/okhttp/issues/3081

相关问题