关于Java:如何禁止在okhttp中将字符集自动添加到Content-Type

How to suppress Charset being automatically added to Content-Type in okhttp

考虑以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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也将为正文[A]声明的字符集也添加到URL中(至少对于文本/纯文本而言),即使未在[B]中声明也是如此。这弄乱了我签名的URL,GCS返回403 Forbidden。

  • 如果我从[A]中删除字符集,则仍会添加它。
  • 如果在我对字符集进行签名之前将其添加到已签名的URL中,则该字符集将起作用并且GCS返回200 OK。

但这不是应该的。至少在使用签名的URL时,这些URL必须完全按照声明的方式发送到服务器。

我尝试使用Apache http客户端(我不想在生产环境中使用它,因为okhttpclient已经是我的安装的一部分),并且该客户端没有暴露此行为:

1
2
3
4
5
6
7
8
9
10
        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或在主体内冗余地传输Content-Type?


我找到了解决方法:

以下是罪魁祸首:

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

create具有3个媒体签名:

  • 细绳
  • 字节[]
  • 文件

当我传递一个String时,它会忽略提供的mediaType并将字符集添加到其中。即使对于图像/ jpeg,它也会发送

图片/ jpeg;字符集= utf-8

到服务器。

使用byte []或File抑制该行为。

希望这对您有所帮助!

[愚蠢的我-为简单起见,我在测试过程中给了它一个String,因为我并不关心身体;-(]


创建请求正文时,只需将内容类型设置为" null ",然后添加标题手册,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
    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中找到了以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   /**
     * 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)
    }