19

考虑以下代码:

    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(至少对于 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 或在正文中冗余传输 Content-Type?

4

2 回答 2

27

我找到了解决方案:

以下行是罪魁祸首:

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

create 有 3 个媒体签名:

  • 细绳
  • 字节[]
  • 文件

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

图像/JPEG;字符集=utf-8

到服务器。

使用 byte[] 或 File 会抑制这种行为。

我希望这可以帮助你!

[愚蠢的我-为简单起见,我在测试期间给了它一个字符串,因为我不在乎身体;-(]

于 2014-08-29T02:14:27.377 回答
2

创建请求体时,只需将内容类型设置为“null”,并添加标题手册,如下所示:

    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)
    }
于 2020-12-16T09:34:36.500 回答