Как отключить автоматическое добавление Charset в Content-Type в охттп

Рассмотрим следующий код:

    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();

Я получаю доступ к GCS с клиента по ранее подписанному URL.

Проблема: Кажется, okhttp добавляет кодировку, объявленную для тела [A], к URL-адресу (по крайней мере, для текста / обычного), даже если он не объявлен в [B]. Это портит мой подписанный URL, и GCS возвращает 403 Forbidden.

  • Если я удалю кодировку из [A], она все равно будет добавлена.
  • Если я добавлю кодировку к подписанному URL-адресу до его подписи, он сработает, и GCS вернет 200 OK.

Но это не так, как должно быть. По крайней мере, при работе с подписанными URL-адресами они должны быть отправлены на сервер точно так, как объявлено.

Я попытался использовать HTTP-клиент Apache (который я не хочу использовать в производственном процессе, так как 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 в теле избыточно?

3 ответа

Я нашел решение:

Следующая строка является виновником:

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

create имеет 3 подписи для медиа:

  • строка
  • байт[]
  • файл

Когда я передаю String, он игнорирует предоставленный mediaType и добавляет к нему кодировку. Даже для изображения / JPEG было бы отправить

изображение / JPEG; кодировка = UTF-8

на сервер.

Использование byte[] или File подавляет такое поведение.

Я надеюсь, это поможет вам!

[Глупый я - для простоты я дал ему String во время тестирования, так как я не заботился о теле;-( ]

Когда вы создаете тело запроса, просто установите для типа содержимого значение «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)
    }

Вы можете зарегистрировать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);
    }

}

Главный.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();
[...]

Обсуждается это на Github okhttp. https://github.com/square/okhttp/issues/3081

Другие вопросы по тегам