Рубрики
Без рубрики

Руководство по ОХТТП

Основы использования OkHttp – изучение отправки различных типов HTTP-запросов, получение и интерпретация HTTP-ответов, а также настройка клиента.

Автор оригинала: baeldung.

1. введение

В этой статье мы покажем основы отправки различных типов HTTP-запросов, получения и интерпретации HTTP-ответов , и как настроить клиент с помощью OkHttp .

Кроме того, мы рассмотрим более продвинутые варианты использования настройки клиента с пользовательскими заголовками, тайм-аутами, кэшированием ответов и т. Д.

2. Обзор OHTTP

OkHttp-это эффективный клиент HTTP & HTTP/2 для приложений Android и Java.

Он поставляется с расширенными функциями, такими как пул соединений (если HTTP/2 недоступен), прозрачное сжатие GZIP и кэширование ответов, чтобы полностью избежать сети для повторных запросов.

Он также способен восстанавливаться после распространенных проблем с подключением, и при сбое соединения, если служба имеет несколько IP-адресов, он может повторить запрос на альтернативные адреса.

На высоком уровне клиент предназначен как для блокировки синхронных, так и для неблокирующих асинхронных вызовов.

Ok Http поддерживает Android 2.3 и выше. Для Java минимальное требование составляет 1.7.

После этого краткого обзора давайте рассмотрим некоторые примеры использования .

3. Зависимость Maven

Давайте сначала добавим библиотеку в качестве зависимости в pom.xml :


    com.squareup.okhttp3
    okhttp
    4.9.1

Чтобы ознакомиться с последней зависимостью этой библиотеки, перейдите на страницу Maven Central .

4. Синхронное ПОЛУЧЕНИЕ С ОХТТП

Чтобы отправить синхронный запрос GET, нам нужно построить объект Request на основе URL и сделать Вызов . После его выполнения мы получаем обратно экземпляр Response :

@Test
public void whenGetRequest_thenCorrect() throws IOException {
    Request request = new Request.Builder()
      .url(BASE_URL + "/date")
      .build();

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

    assertThat(response.code(), equalTo(200));
}

5. Асинхронная РАБОТА С OHTTP

Теперь, чтобы сделать асинхронный GET, нам нужно поставить в очередь Вызов ./| Обратный вызов позволяет нам читать ответ, когда он читаем. Это происходит после того, как заголовки ответов готовы.

Чтение тела ответа все еще может быть заблокировано. В настоящее время OkHttp не предлагает никаких асинхронных API для получения тела ответа по частям:

@Test
public void whenAsynchronousGetRequest_thenCorrect() {
    Request request = new Request.Builder()
      .url(BASE_URL + "/date")
      .build();

    Call call = client.newCall(request);
    call.enqueue(new Callback() {
        public void onResponse(Call call, Response response) 
          throws IOException {
            // ...
        }
        
        public void onFailure(Call call, IOException e) {
            fail();
        }
    });
}

6. ПОЛУЧИТЬ С Параметрами Запроса

Наконец, чтобы добавить параметры запроса в наш запрос GET, мы можем воспользоваться Http Url.Builder .

После построения URL-адреса мы можем передать его нашему объекту Request :

@Test
public void whenGetRequestWithQueryParameter_thenCorrect() 
  throws IOException {
    
    HttpUrl.Builder urlBuilder 
      = HttpUrl.parse(BASE_URL + "/ex/bars").newBuilder();
    urlBuilder.addQueryParameter("id", "1");

    String url = urlBuilder.build().toString();

    Request request = new Request.Builder()
      .url(url)
      .build();
    Call call = client.newCall(request);
    Response response = call.execute();

    assertThat(response.code(), equalTo(200));
}

7. Запрос на публикацию

Давайте рассмотрим простой запрос POST, в котором мы создаем RequestBody для отправки параметров “имя пользователя” и “пароль” :

@Test
public void whenSendPostRequest_thenCorrect() 
  throws IOException {
    RequestBody formBody = new FormBody.Builder()
      .add("username", "test")
      .add("password", "test")
      .build();

    Request request = new Request.Builder()
      .url(BASE_URL + "/users")
      .post(formBody)
      .build();

    Call call = client.newCall(request);
    Response response = call.execute();
    
    assertThat(response.code(), equalTo(200));
}

В нашей статье Краткое руководство по отправке запросов с помощью OkHttp содержит больше примеров запросов на отправку с помощью OkHttp.

8. Загрузка файлов

8.1. Загрузить файл

В этом примере мы увидим, как загрузить файл /. Мы загрузим файл “ test.text” , используя Составное тело.Строитель :

@Test
public void whenUploadFile_thenCorrect() throws IOException {
    RequestBody requestBody = new MultipartBody.Builder()
      .setType(MultipartBody.FORM)
      .addFormDataPart("file", "file.txt",
        RequestBody.create(MediaType.parse("application/octet-stream"), 
          new File("src/test/resources/test.txt")))
      .build();

    Request request = new Request.Builder()
      .url(BASE_URL + "/users/upload")
      .post(requestBody)
      .build();

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

    assertThat(response.code(), equalTo(200));
}

8.2. Получить Ход Загрузки Файла

Наконец, давайте посмотрим, как получить прогресс загрузки файла . Мы расширим Тело запроса , чтобы получить представление о процессе загрузки.

Во-первых, вот метод загрузки:

@Test
public void whenGetUploadFileProgress_thenCorrect() 
  throws IOException {
    RequestBody requestBody = new MultipartBody.Builder()
      .setType(MultipartBody.FORM)
      .addFormDataPart("file", "file.txt",
        RequestBody.create(MediaType.parse("application/octet-stream"), 
          new File("src/test/resources/test.txt")))
      .build();
      
    ProgressRequestWrapper.ProgressListener listener 
      = (bytesWritten, contentLength) -> {
        float percentage = 100f * bytesWritten / contentLength;
        assertFalse(Float.compare(percentage, 100) > 0);
    };

    ProgressRequestWrapper countingBody
      = new ProgressRequestWrapper(requestBody, listener);

    Request request = new Request.Builder()
      .url(BASE_URL + "/users/upload")
      .post(countingBody)
      .build();

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

    assertThat(response.code(), equalTo(200));
}

Вот интерфейс ProgressListener , который позволяет нам наблюдать за ходом загрузки:

public interface ProgressListener {
    void onRequestProgress(long bytesWritten, long contentLength);
}

Вот Progress RequestWrapper , который является расширенной версией Тела запроса :

public class ProgressRequestWrapper extends RequestBody {

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        BufferedSink bufferedSink;

        countingSink = new CountingSink(sink);
        bufferedSink = Okio.buffer(countingSink);

        delegate.writeTo(bufferedSink);

        bufferedSink.flush();
    }
}

Наконец, вот раковина подсчета , которая является расширенной версией пересылки Раковины :

protected class CountingSink extends ForwardingSink {

    private long bytesWritten = 0;

    public CountingSink(Sink delegate) {
        super(delegate);
    }

    @Override
    public void write(Buffer source, long byteCount)
      throws IOException {
        super.write(source, byteCount);
        
        bytesWritten += byteCount;
        listener.onRequestProgress(bytesWritten, contentLength());
    }
}

Обратите внимание, что:

  • При расширении Forwarding Sink до “CountingSink”, мы переопределяем метод write() для подсчета записанных (переданных) байтов
  • При расширении RequestBody до ” Progress RequestWrapper “мы переопределяем метод writeTo (), чтобы использовать наш ” ForwardingSink”

9. Настройка пользовательского заголовка

9.1. Установка заголовка запроса

Чтобы задать любой пользовательский заголовок для Запроса , мы можем использовать простой AddHeader вызов:

@Test
public void whenSetHeader_thenCorrect() throws IOException {
    Request request = new Request.Builder()
      .url(SAMPLE_URL)
      .addHeader("Content-Type", "application/json")
      .build();

    Call call = client.newCall(request);
    Response response = call.execute();
    response.close();
}

9.2. Установка заголовка по умолчанию

В этом примере мы увидим, как настроить заголовок по умолчанию на самом клиенте, а не устанавливать его для каждого запроса.

Например, если мы хотим установить тип контента “application/json” для каждого запроса, нам нужно установить перехватчик для нашего клиента. Вот метод:

@Test
public void whenSetDefaultHeader_thenCorrect() 
  throws IOException {
    
    OkHttpClient client = new OkHttpClient.Builder()
      .addInterceptor(
        new DefaultContentTypeInterceptor("application/json"))
      .build();

    Request request = new Request.Builder()
      .url(SAMPLE_URL)
      .build();

    Call call = client.newCall(request);
    Response response = call.execute();
    response.close();
}

А вот ContentTypeInterceptor по умолчанию , который является расширенной версией Перехватчика :

public class DefaultContentTypeInterceptor implements Interceptor {
    
    public Response intercept(Interceptor.Chain chain) 
      throws IOException {

        Request originalRequest = chain.request();
        Request requestWithUserAgent = originalRequest
          .newBuilder()
          .header("Content-Type", contentType)
          .build();

        return chain.proceed(requestWithUserAgent);
    }
}

Обратите внимание, что перехватчик добавляет заголовок к исходному запросу.

10. Не Следуйте Перенаправлениям

В этом примере мы увидим, как настроить OkHttpClient для остановки следующих перенаправлений.

По умолчанию, если на запрос GET отвечает HTTP 301, перемещенный навсегда , перенаправление выполняется автоматически. В некоторых случаях это может быть совершенно нормально, но, безусловно, есть случаи, когда это нежелательно.

Чтобы добиться такого поведения, когда мы создаем наш клиент, нам нужно установить followRedirects в false .

Обратите внимание, что в ответе будет возвращен код состояния HTTP 301 :

@Test
public void whenSetFollowRedirects_thenNotRedirected() 
  throws IOException {

    OkHttpClient client = new OkHttpClient().newBuilder()
      .followRedirects(false)
      .build();
    
    Request request = new Request.Builder()
      .url("http://t.co/I5YYd9tddw")
      .build();

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

    assertThat(response.code(), equalTo(301));
}

Если мы включим перенаправление с параметром true (или полностью удалим его), клиент будет следовать перенаправлению, и тест завершится неудачей, так как код возврата будет HTTP 200.

11. Тайм-ауты

Используйте тайм-ауты для сбоя вызова, когда его одноранговый узел недоступен. Сетевые сбои могут быть вызваны проблемами с подключением клиентов, проблемами доступности серверов или чем-либо между ними. OkHttp поддерживает тайм-ауты подключения, чтения и записи.

В этом примере мы построили наш клиент с таймаутом чтения в 1 секунду, в то время как URL-адрес обслуживается с задержкой в 2 секунды:

@Test
public void whenSetRequestTimeout_thenFail() 
  throws IOException {
    OkHttpClient client = new OkHttpClient.Builder()
      .readTimeout(1, TimeUnit.SECONDS)
      .build();

    Request request = new Request.Builder()
      .url(BASE_URL + "/delay/2")
      .build();
 
    Call call = client.newCall(request);
    Response response = call.execute();

    assertThat(response.code(), equalTo(200));
}

Обратите внимание, что тест завершится неудачно, так как время ожидания клиента меньше времени отклика ресурса.

12. Отмена вызова

Используйте Call.cancel () , чтобы немедленно остановить текущий вызов. Если поток в данный момент пишет запрос или читает ответ, будет выдано исключение IOException .

Используйте это для сохранения сети, когда вызов больше не нужен; например, когда пользователь переходит из приложения:

@Test(expected = IOException.class)
public void whenCancelRequest_thenCorrect() 
  throws IOException {
    ScheduledExecutorService executor
      = Executors.newScheduledThreadPool(1);

    Request request = new Request.Builder()
      .url(BASE_URL + "/delay/2")  
      .build();

    int seconds = 1;
    long startNanos = System.nanoTime();

    Call call = client.newCall(request);

    executor.schedule(() -> {
        logger.debug("Canceling call: "  
            + (System.nanoTime() - startNanos) / 1e9f);

        call.cancel();
            
        logger.debug("Canceled call: " 
            + (System.nanoTime() - startNanos) / 1e9f);
        
    }, seconds, TimeUnit.SECONDS);

    logger.debug("Executing call: " 
      + (System.nanoTime() - startNanos) / 1e9f);

    Response response = call.execute();
	
    logger.debug(Call was expected to fail, but completed: " 
      + (System.nanoTime() - startNanos) / 1e9f, response);
}

13. Кэширование ответов

Чтобы создать Кэш , нам понадобится каталог кэша, в который мы можем читать и записывать, а также ограничение на размер кэша.

Клиент будет использовать его для кэширования ответа:

@Test
public void  whenSetResponseCache_thenCorrect() 
  throws IOException {
    int cacheSize = 10 * 1024 * 1024;

    File cacheDirectory = new File("src/test/resources/cache");
    Cache cache = new Cache(cacheDirectory, cacheSize);

    OkHttpClient client = new OkHttpClient.Builder()
      .cache(cache)
      .build();

    Request request = new Request.Builder()
      .url("http://publicobject.com/helloworld.txt")
      .build();

    Response response1 = client.newCall(request).execute();
    logResponse(response1);

    Response response2 = client.newCall(request).execute();
    logResponse(response2);
}

После запуска теста ответ от первого вызова не будет кэширован. Вызов метода cache Response вернет null , в то время как вызов метода network Response вернет ответ из сети.

Кроме того, папка кэша будет заполнена файлами кэша.

Выполнение второго вызова приведет к противоположному эффекту, так как ответ уже будет кэширован. Это означает, что вызов network Response вернет null , в то время как вызов cache Response вернет ответ из кэша.

Чтобы предотвратить использование кэша для ответа, используйте Cache Control.FORCE_NETWORK . Чтобы предотвратить его использование в сети, используйте Cache Control.FORCE_CACHE .

Имейте в виду: если вы используете FORCE_CACHE и для ответа требуется сеть, OkHttp вернет 504 неудовлетворительных ответа на запрос.

14. Заключение

В этой статье мы рассмотрели несколько примеров того, как использовать OkHttp в качестве клиента HTTP & HTTP/2.

Как всегда, пример кода можно найти в проекте GitHub .