1. Метод send(), BodyHandlers

Оскільки ми закінчили вивчення того, як формувати http-запит, можемо переходити до найголовнішого – надсилання цього запиту. У найпростішому випадку це зробити легко:


HttpRequest request = HttpRequest.newBuilder(new URI("https://javarush.com")).build();
 
   HttpClient client = HttpClient.newBuilder()
        .version(Version.HTTP_1_1)
        .build();
 
   HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
   System.out.println(response.statusCode() );
   System.out.println(response.body() ); 

А що ж це за BodyHandlers такі? А як ти вважаєш? Ти надіслав запит, отже, тобі має прийти відповідь – http response. І ця відповідь може бути response body: рядок, файл, масив байт, InputStream.

Так, все правильно. Так само, як і під час формування запиту, тобі потрібно вказати тип response body відповіді. Загалом їх може бути 8 штук:

  • BodyHandlers.ofByteArray
  • BodyHandlers.ofString
  • BodyHandlers.ofFile
  • BodyHandlers.discarding
  • BodyHandlers.replacing
  • BodyHandlers.ofLines
  • BodyHandlers.fromLineSubscriber

Залежно від цього, який тип BodyHandlers ти передаєш до методу send(), такий тип результату він і поверне. Приклад:


   // response body ігнорується
   HttpResponse<Void> response = client.send(request, BodyHandlers.discarding());

  // response body це рядок
   HttpResponse<String>response = client.send(request, BodyHandlers.ofString());
 

   // response body це файл
   HttpResponse<Path> response = client.send(request, BodyHandlers.ofFile(Paths.get("readme.txt")));

   // response body це InputStream
   HttpResponse<InputStream> response = client.send(request, BodyHandlers.ofInputStream());

Якщо відповіддю тобі мають надіслати файл, то до методу BodyHandlers.ofFile() тобі потрібно передати ім'я локального файлу, до якого він збережеться об'єктом HttpClient.

2. Метод followRedirects()

Також під час надсилання запиту ти можеш вказати, що робити HttpClient-у, якщо сервер у відповідь надішле 301 або 302 (тимчасовий або постійний редирект). Уяви, що сервер надіслав код 302, і тобі потрібно: відстежити цю ситуацію, отримати нову URL-адресу з відповіді і надіслати запит на нову адресу.

Не дуже хотілося б таким займатися, особливо враховуючи, що ця ситуація зустрічається часто і вже давно автоматизована у всіх http-клієнтах. Також і в цьому HttpClient тобі потрібно лише вказати, який режим редиректу ти обираєш при відправці запиту.


HttpResponse response = HttpClient.newBuilder()
  .followRedirects( HttpClient.Redirect.ALWAYS )
  .build()
  .send(request, BodyHandlers.ofString());

Є лише 3 варіанти для редиректу:

  • ALWAYS – завжди;
  • NEVER – ніколи;
  • NORMAL – завжди, окрім HTTPS -> HTTP.

Як бачиш, варіантів тут небагато, але завжди краще мати можливість налаштування, аніж не мати її.

3. Метод proxy()

Є ще пара корисних опцій, які використовуються рідше. Вони тобі не потрібні рівно доти, доки не стануть потрібні :)

Перший – це proxy. У звичайному житті ти нечасто стикаєшся з ними, але багато великих корпорацій мають складну систему безпеки інтернет-трафіку, а отже й різні налаштування proxy.

Ну, і звісно, твій софт, який працюватиме десь у надрах такої корпорації, колись стикнеться з тим, що йому потрібно буде задіяти proxy. Тому добре, що така опція тут також є.

Налаштувати проксі дуже просто – ось приклад:


HttpResponse<String> response = HttpClient.newBuilder()
  .proxy( ProxySelector.getDefault())
  .build()
  .send(request, BodyHandlers.ofString());

Тут обрано proxy за замовчуванням, але ти можеш захотіти налаштувати свій:


HttpResponse response = HttpClient.newBuilder()
  .proxy(ProxySelector.of(new InetSocketAddress("proxy.microsoft.com", 80)))
  .build()
  .send(request, BodyHandlers.ofString());

Як саме працювати з proxy ми розглядати не будемо, оскільки це виходить за межі цього курсу.

4. authenticator()

І ще один важливий момент. HTTP-протокол підтримує автентифікацію. Прямо лише на рівні протоколу.

Наразі таким підходом майже не користуються, але років 20 тому він був поширений. Виглядав такий Http-запит таким чином:


        http://username@example.com/
        

Або навіть так:


        http://username:password@example.com/
        

Це не пошта. Це саме заслання. І так, тобі не здалося. Логін і навіть пароль можна було зазначити прямо в http-запиті. Та й зараз можна. Тому я й пишу, що зараз так зазвичай ніхто не робить. Але така можливість є.


Authenticator auth = new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
    return new PasswordAuthentication(
     "username",
        "password".toCharArray());
    }
};

HttpResponse<String> response = HttpClient.newBuilder()
  .authenticator(auth).build()
  .send(request, BodyHandlers.ofString());

Це цікаво! Знаєш, чому замість “password" коду написано "password".toCharArray()?

Тому що другий параметр конструктора PasswordAuthentication – не String, а CharArray.

А чому другий параметр не String, а CharArray?

Тому що всі паролі з метою безпеки забороняється зберігати у вигляді цілого рядка навіть у своєму власному застосунку. Тобто твоя програма у власній пам'яті не повинна зберігати пароль у вигляді рядка. Щоб якщо хтось зробив damp пам'яті, з нього не можна було витягнути пароль.

Але водночас пароль можна передати за допомогою незахищеного HTTP-протоколу через півсвіту :)

Що ж. Світ не є ідеальним.

Детальніше з цією темою ти можеш ознайомитися за посиланнями:

HTTP автентифікація

Understanding HTTP Authentication