Публикация с помощью Java HttpClient
API Java HttpClient был представлен в Java 11. API реализует клиентскую часть самых последних стандартов HTTP . Он поддерживает HTTP/1.1 и HTTP/2, как синхронную, так и асинхронную модели программирования.
Мы можем использовать его для отправки HTTP-запросов и получения их ответов. До Java 11 нам приходилось полагаться на элементарную реализацию URLConnection или сторонние библиотеки, такие как Apache HttpClient .
В этом руководстве мы рассмотрим отправку POST-запросов с помощью Java HttpClient . Мы покажем, как отправлять как синхронные, так и асинхронные запросы POST, а также параллельные запросы POST. Кроме того, мы проверим, как добавлять параметры аутентификации и тела JSON в запросы POST.
Наконец, мы увидим, как загружать файлы и отправлять данные формы. Поэтому мы рассмотрим большинство распространенных вариантов использования.
2. Подготовка POST-запроса
Прежде чем мы сможем отправить HTTP-запрос, нам сначала нужно создать экземпляр HttpClient .
Экземпляры HttpClient можно настроить и создать из его построителя с помощью метода newBuilder . В противном случае, если настройка не требуется, мы можем использовать метод утилиты newHttpClient для создания клиента по умолчанию:
HttpClient client = HttpClient.newHttpClient();
HttpClient будет использовать HTTP/2 по умолчанию. Он также автоматически понизится до HTTP/1.1, если сервер не поддерживает HTTP/2.
Теперь мы готовы создать экземпляр HttpRequest из его построителя. Позже мы воспользуемся экземпляром клиента для отправки этого запроса. Минимальные параметры для POST-запроса — это URL-адрес сервера, метод запроса и тело:
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(serviceUrl)) .POST(HttpRequest.BodyPublishers.noBody()) .build();
Тело запроса должно быть предоставлено через класс BodyPublisher . Это издатель реактивного потока, который публикует потоки тела запроса по запросу. В нашем примере мы использовали публикатор тела, который не отправляет тело запроса.
3. Отправка POST-запроса
Теперь, когда мы подготовили запрос POST, давайте рассмотрим различные варианты его отправки.
3.1. Синхронно
Мы можем отправить подготовленный запрос, используя этот метод отправки по умолчанию. Этот метод заблокирует наш код до тех пор, пока не будет получен ответ :
HttpResponseString> response = client.send(request, HttpResponse.BodyHandlers.ofString())
Утилита BodyHandlers реализует различные полезные обработчики, такие как обработка тела ответа как строки или потоковая передача тела ответа в файл. После получения ответа объект HttpResponse будет содержать статус ответа, заголовки и тело:
assertThat(response.statusCode()) .isEqualTo(200); assertThat(response.body()) .isEqualTo("");
3.2. Асинхронно
Мы могли бы отправить тот же запрос из предыдущего примера асинхронно, используя метод sendAsync . Вместо того, чтобы блокировать наш код, этот метод сразу вернет экземпляр CompletableFuture : ****
CompletableFutureHttpResponseString>> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
CompletableFuture завершается с HttpResponse , как только он становится доступным:
HttpResponseString> response = futureResponse.get(); assertThat(response.statusCode()).isEqualTo(200); assertThat(response.body()).isEqualTo("");
3.3. Одновременно
Мы можем комбинировать Streams с CompletableFutures , чтобы выдавать несколько запросов одновременно и ждать их ответов :
ListCompletableFutureHttpResponseString>>> completableFutures = serviceUrls.stream() .map(URI::create) .map(HttpRequest::newBuilder) .map(builder -> builder.POST(HttpRequest.BodyPublishers.noBody())) .map(HttpRequest.Builder::build) .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString())) .collect(Collectors.toList());
Теперь давайте дождемся завершения всех запросов, чтобы мы могли обработать их ответы сразу:
CompletableFutureListHttpResponseString>>> combinedFutures = CompletableFuture .allOf(completableFutures.toArray(new CompletableFuture[0])) .thenApply(future -> completableFutures.stream() .map(CompletableFuture::join) .collect(Collectors.toList()));
Поскольку мы объединили все ответы, используя методы allOf и join , мы получили новый CompletableFuture , который содержит наши ответы:
ListHttpResponseString>> responses = combinedFutures.get(); responses.forEach((response) -> assertThat(response.statusCode()).isEqualTo(200); assertThat(response.body()).isEqualTo(""); >);
4. Добавление параметров аутентификации
Мы можем установить аутентификатор на уровне клиента для HTTP-аутентификации для всех запросов :
HttpClient client = HttpClient.newBuilder() .authenticator(new Authenticator() @Override protected PasswordAuthentication getPasswordAuthentication() return new PasswordAuthentication( "foreach", "123456".toCharArray()); > >) .build();
Однако HttpClient не отправляет базовые учетные данные, пока не будет запрошен для них с заголовком WWW-Authenticate с сервера.
Чтобы обойти это, мы всегда можем создать и отправить основной заголовок авторизации вручную:
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(serviceUrl)) .POST(HttpRequest.BodyPublishers.noBody()) .header("Authorization", "Basic " + Base64.getEncoder().encodeToString(("foreach:123456").getBytes())) .build();
5. Добавление тела
В примерах до сих пор мы не добавляли никаких тел к нашим POST-запросам. Однако метод POST обычно используется для отправки данных на сервер через тело запроса .
5.1. JSON-текст
Утилита BodyPublishers реализует различные полезные средства публикации, такие как публикация тела запроса из строки или файла. Мы можем опубликовать данные JSON как String , преобразованные с использованием набора символов UTF-8:
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(serviceUrl)) .POST(HttpRequest.BodyPublishers.ofString("")) .build();
5.2. Загрузка файлов
Давайте создадим временный файл , который мы можем использовать для загрузки через HttpClient :
Path file = tempDir.resolve("temp.txt"); ListString> lines = Arrays.asList("1", "2", "3"); Files.write(file, lines);
HttpClient предоставляет отдельный метод BodyPublishers.ofFile для добавления файла в тело POST . Мы можем просто добавить наш временный файл в качестве параметра метода, а API позаботится об остальном:
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(serviceUrl)) .POST(HttpRequest.BodyPublishers.ofFile(file)) .build();
5.3. Отправка форм
В отличие от файлов, HttpClient не предоставляет отдельного метода для публикации данных формы. Поэтому нам снова нужно использовать метод BodyPublishers.ofString :
MapString, String> formData = new HashMap>(); formData.put("username", "foreach"); formData.put("message", "hello"); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(serviceUrl)) .POST(HttpRequest.BodyPublishers.ofString(getFormDataAsString(formData))) .build();
Однако нам нужно преобразовать данные формы из Map в String , используя пользовательскую реализацию:
private static String getFormDataAsString(MapString, String> formData) StringBuilder formBodyBuilder = new StringBuilder(); for (Map.EntryString, String> singleEntry : formData.entrySet()) if (formBodyBuilder.length() > 0) formBodyBuilder.append("&"); > formBodyBuilder.append(URLEncoder.encode(singleEntry.getKey(), StandardCharsets.UTF_8)); formBodyBuilder.append("="); formBodyBuilder.append(URLEncoder.encode(singleEntry.getValue(), StandardCharsets.UTF_8)); > return formBodyBuilder.toString(); >
6. Заключение
В этой статье мы рассмотрели отправку POST-запросов с использованием Java HttpClient API, представленного в Java 11 .
Мы узнали, как создать экземпляр HttpClient и подготовить POST-запрос. Мы увидели, как отправлять подготовленные запросы синхронно, асинхронно и параллельно. Далее мы также увидели, как добавить основные параметры аутентификации.
Наконец, мы рассмотрели добавление тела к запросу POST. Мы рассмотрели полезные нагрузки JSON, загрузку файлов и отправку данных формы.
Как всегда, полный исходный код доступен на GitHub .
- 1. Обзор
- 2. Подготовка POST-запроса
- 3. Отправка POST-запроса
- 3.1. Синхронно
- 3.2. Асинхронно
- 3.3. Одновременно
- 5.1. JSON-текст
- 5.2. Загрузка файлов
- 5.3. Отправка форм
Как отправить post запрос java
Вот пример кода, который отправляет POST-запрос на сервер и выводит ответ:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; public class HttpPostRequest public static void main(String[] args) try // Создаем URL-адрес для запроса URL url = new URL("http://www.example.com/api"); // Создаем объект HttpURLConnection и настраиваем его HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); // Создаем тело запроса String body = "param1=value1¶m2=value2"; // Записываем тело запроса в поток вывода OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); writer.write(body); writer.flush(); // Считываем ответ от сервера BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) response.append(line); > reader.close(); // Выводим ответ от сервера System.out.println(response.toString()); > catch (Exception e) e.printStackTrace(); > > >- Здесь мы создаем объект HttpURLConnection и настраиваем его для отправки POST-запроса
- Затем мы записываем тело запроса в поток вывода и считываем ответ от сервера с помощью BufferedReader
- В конце мы выводим ответ от сервера на экран.
Обратите внимание, что в примере мы отправляем запрос с параметрами param1 и param2 , но вы можете изменить их на свои параметры в зависимости от вашего запроса.
Добавление записи через POST-запрос в Spring Boot
В статье Работа с БД в Spring Boot на примере postgresql мы узнали как читать данные из БД. Но чтение данных – это лишь малая часть всех операций, которые встречаются в типичном java-приложении. Теперь попробуем создать полноценный rest-интерфейс для добавления новых записей, их модификации и удаления.
За основу возьмём наше приложение из указанной статьи. Оно состоит из трёх слоёв: работа с БД (repository), бизнес-логика приложения (service) и сам rest-интерфейс (controller), который обрабатывает входящий json и генерирует исходящий.
Репозиторий
Начнём с доработки репозитория (интерфейс ProfileRepository), где до сих пор был только один метод чтения данных. Добавим метод вставки записи в БД:
void insertProfile(String firstName, String secondName, int age);
При добавлении новой записи нам достаточно всего три поля. id будет сгенерирован в БД автоматически.
В реализацию интерфейса ProfileRepositoryImpl добавляем sql-запросы в виде констант, которые принято размещать в начале класса:
private static final String SQL_INSERT_PROFILE =
«insert into profile (first_name, last_name, age) values (:firstName, :lastName, :age)» ;Имена параметров, которые мы будем подставлять в sql-запрос, начинаются с двоеточия.
Вот реализация добавления записи в БД:
@Override
public void insertProfile(String firstName, String lastName, int age) var params = new MapSqlParameterSource();
params.addValue( «firstName» , firstName);
params.addValue( «lastName» , lastName);
params.addValue( «age» , age);
jdbcTemplate.update( SQL_INSERT_PROFILE , params);
>Всё предельно просто: сначала собираем параметры, затем передаём их вместе с запросом в метод jdbcTemplate.update(), который вопреки своему названию отвечает вообще за любые изменения данных (и insert, и update, и delete). Обратите внимание, что jdbcTemplate у нас имеет тип NamedParameterJdbcTemplate – именно он позволяет использовать именованные параметры. Иначе пришлось бы писать знаки вопроса.
Сервисный слой
Перейдём к сервисному слою. Интерфейс ProfileService не отличается оригинальностью:
void createProfile(String firstName, String secondName, int age);
Его реализация в ProfileServiceImpl. Здесь по сути только вызываем метод репозитория:
@Override
public void createProfile(String firstName, String secondName, int age) profileRepository.insertProfile(firstName, secondName, age);
>Контроллер
Согласно архитектуре restful-сервисов, чтение данных мы делаем при помощи GET-запросов, а создание – при помощи POST. Также хотелось бы отметить, что у GET-запроса не может быть тела запроса (body), все его параметры перечисляются в строке запроса. А POST-запрос может иметь тело, в которое мы будем помещать целевой json.
Сперва нам нужно добавить в проект ещё одну зависимость spring-boot-starter-validation, которая позволяет валидировать входящие запросы:
org.springframework.boot
spring-boot-starter-validation Перейдём к нашему контроллеру ProfileController и добавим в него метод создания профиля.
@PostMapping
@ResponseStatus (HttpStatus. CREATED )
public void createProfile( @Valid @RequestBody ProfileRequest request) profileService.createProfile(
request.firstName(),
request.lastName(),
request.age()
);
>В качестве аргумента метод принимает объект ProfileRequest, реализованный как record-class:
public record ProfileRequest(
@NotNull
String firstName,
@NotNull
String lastName,
@NotNull
@Min ( 1 )
Integer age
) >Эта модель представляет собой ровно тот объект, который мы ожидаем в теле запроса. Имена полей в json должны совпадать с именами полей в классе (при желании это можно переопределить дополнительной аннотацией). Дополнительно накладываем ограничения из пакета javax.validation.constraints на каждое поле.
Аннотация @NotNull применима для ссылочных типов и означает обязательность этого поля в запросе. Обратите внимание, что тут мы специально используем ссылочный Integer, который допускает null, а не примитивный int, который по умолчанию равен нулю. Ссылочный числовой тип вместе с @NotNull позволяет проверять обязательность числовых типов.
Аннотация @Min говорит сама за себя и ограничивает числовые типы снизу. Тем самым мы никогда не допустим, чтобы нам передали отрицательное число в качестве возраста.
Чтобы эти проверки работали для входящего json, параметр метода надо снабдить аннотацией @Valid, а чтобы именно в этот класс был преобразован входящий json, также добавляем @RequestBody. Внутри просто вызываем сервисный слой.
Аннотация @PostMapping указывает на то, что это обработчик POST-запроса. В скобках можно указать относительный урл, но в данном случае мы его не указали, поэтому запрос надо будет кидать по тому адресу, который указан на уровне контроллера, то есть /profiles. @ResponseStatus показывает, какой http-код нужно вернуть в ответ. В данном случае мы опять следуем rest-соглашениям и возвращаем код 201 – «Created».
Теперь мы готовы к тому, чтобы выполнить rest-запрос на создание новой записи в БД. Запускаем приложение и отравляем указанный POST-запрос по адресу http://127.0.0.1:8080/profiles. В http-заголовках обязательно указываем Content-Type: application/json.
<
«firstName» : «Петр» ,
«lastName» : «Сидоров» ,
«age» : 14
>В ответ в случае успеха получаем http-статус 201. Если же мы попытаемся нарушить наши ограничения по полям, то получим json с подробным описанием ошибки.
Таким образом, Spring Boot позволяет буквально за 5 минут создать полноценный обработчик POST-запроса с валидацией входящих параметров.
В следующей статье Обновление записи через PUT-запрос в Spring Boot рассмотрим как можно обновлять записи.
Как принять POST запрос?
Как принять POST запрос? Не понимаю как это сделать, нигде не нашёл. Например, нам отсылают данные с помощью API (передают ФИО и номер телефона), а как нам их принять, не совсем понимаю.
Отслеживать
задан 23 окт 2019 в 14:15
1,375 1 1 золотой знак 19 19 серебряных знаков 37 37 бронзовых знаков
23 окт 2019 в 14:42
@Bakhuss если оформите ответ, то я отмечу его как правильным.
7 ноя 2019 в 13:34
Благодарю, но там только ссылка на гайд, которая гуглится на раз. 😉
8 ноя 2019 в 11:451 ответ 1
Сортировка: Сброс на вариант по умолчанию
Для начала необходим UserRepo (необходим, чтобы программа понимала куда отправлять и с чем работает)
public interface UserRepo extends CrudRepository
Дальше Application (запуск нашего Spring)
@SpringBootApplication public class Application < public static void main(String[] args) < SpringApplication.run(Application.class, args); >>UserController (программа принимает и отправляет на преобразование данных вида Json)
@RestController @RequestMapping("/users") //какое угодно слово может быть вместо users public class UserController < @Autowired private UserRepo userRepo; @PostMapping(consumes = ) public String createUser(@Valid @RequestBody User requestUserDetails) < userRepo.save(requestUserDetails); return "Сообщение доставлено."; >>В заключении User (преобразовывает данные)
@Entity @Table(name = "НАЗВАНИЕ БД", schema = "НАЗВАНИЕ СХЕМЫ") public class User < @Id private String phone; private String message; public String getPhone() < return phone; >public void setPhone(String phone) < this.phone = phone; >public String getMessage() < return message; >public void setLastName(String message) < this.message = message; >>