Java 11 HTTP Client API to Consume Restful Web Service Created Using Spring Boot
This article describes how to use Java 11 Http Client API to send HTTP GET/POST/PUT/DELETE requests.
Join the DZone community and get the full member experience.
Join For FreeOne of the features added in Java 11 is the standardized Http Client API. This article describes how to use Java 11 Http Client API to send HTTP GET/POST/PUT/DELETE requests.
This new API supports HTTP / 1.1 as well as HTTP 2. The newer version of the HTTP protocol is designed to improve the overall performance of sending requests by a client and receiving responses from the server. This is achieved by introducing several changes such as stream multiplexing, header compression and push promises.
In JDK 11, a new module is introduced as follows:
module java.net.http {
exports java.net.http;
}
The package contains:
HttpClient
- Used to send requests and receive responses. Provides Synchronous and Asynchronous request mechanisms.
- HttpClient instance is immutable, once created you can send multiple requests with the same.
- To send requests, first you need to create HttpClient.
- Example:
-
Java
xxxxxxxxxx
1
1HttpClient client = HttpClient.newBuilder().version(Version.HTTP_2).build();
- If HTTP/2 is not supported by the server, processes the request using HTTP/1.1
- You can use executor() for asynchronous tasks.
HttpRequest
- Create HttpRequest instance and set the URI, request method optionally specify the body and headers.
- HttpRequest instance is immutable and can be sent multiple times.
- Client supports all HTTP methods. Methods available to make different requests are GET(), POST(), PUT(), DELETE(), method().
- Example:
-
Java
xxxxxxxxxx
1
1HttpRequest request = HttpRequest.newBuilder(URI.create(“http://localhost:8080/addDetails”))
2.header("Content-Type", "application/json")
3.POST(HttpRequest.BodyPublishers.ofString(inputJson)).build();
4
HttpRequest.BodyPublisher/HttpRequest.BodyPublishers
- BodyPublisher is used to send the request with a request body.
- BodyPublishers are responsible for publishing the body content from a String or a File.
- In the above example, created BodyPublisher using BodyPublishers.ofString() and passing it as an argument to POST() method.
HttpResponse
- Once after HttpRequest is sent by the client, HttpResponse is received which includes headers and a message body, if any.
- Example:
Java
xxxxxxxxxx
1
1CompletableFuture<HttpResponse<String>> response = client.sendAsync(request,HttpResponse.BodyHandlers.ofString());
- sendAsync() is used to send the HttpRequest Asynchronously(non-blocking) and returns CompletableFuture.
HttpResponse.BodyHandler/HttpResponse.BodyHandlers
- HttpResponse.BodyHandler determines how to handle response body. The BodyHandler is invoked once the response status code and headers are available, but before the response body bytes are received. The BodyHandler is responsible for creating the BodySubscriber.
- HttpResponse.BodyHandlers provides factory methods for creating Body Handler. Accumulates the response bytes in memory until it is completely received, after which it is converted into the high-level java type like String.
HttpResponse.BodySubscriber
- HttpResponse.BodySubscriber is a reactive-stream subscriber that receives streams of data with non-blocking back pressure.
Consider you have created a RESTful web service using Spring Boot. You can use Java 11 Http Client API to send the request asynchronously and to receive the response. No need to add any external library to your project.
Software Required:
- Eclipse
- JDK 11
- MySQL 8.0
Create RESTful web service application using Spring Boot 2.1.x with the following project structure.
1. You can refer POM.xml here:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath></relativePath> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.productservice</groupId>
<artifactId>ProductService</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ProductService</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. Mention the below in application.properties file.
xxxxxxxxxx
spring.datasource.url= jdbc:mysql://localhost:3307/springrestdemos
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto = create
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
3. Create ProductBean class in com.example.productservice.business.bean package.
xxxxxxxxxx
public class ProductBean {
private int id;
private String name;
private String description;
private Double price;
// getters and setters
// toString()
}
4. Create ProductController class in com.example.productservice.controller package.
xxxxxxxxxx
package com.example.productservice.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.productservice.business.bean.ProductBean;
import com.example.productservice.service.ProductServiceImpl;
public class ProductController {
private ProductServiceImpl productServiceImpl;
value = "product/controller/getDetails", produces = MediaType.APPLICATION_JSON_VALUE ) (
public ResponseEntity<List<ProductBean>> getProductDetails() {
List<ProductBean> listProduct = new ArrayList<ProductBean>(productServiceImpl.getAllProduct());
return new ResponseEntity<List<ProductBean>>(listProduct,HttpStatus.OK);
}
value="product/controller/getDetailsById/{id}", produces = MediaType.APPLICATION_JSON_VALUE) (
public ResponseEntity<ProductBean> getProductDetailByEmployeeId( ("id") int myId) {
ProductBean product = productServiceImpl.getProductDetailsById(myId);
if (product != null) {
return new ResponseEntity<ProductBean>(product, HttpStatus.OK);
} else {
return new ResponseEntity<ProductBean>(HttpStatus.NOT_FOUND);
}
}
value="product/controller/addProduct", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_HTML_VALUE) (
public ResponseEntity<String> addProduct( ProductBean product) {
int count=productServiceImpl.addProduct(product);
return new ResponseEntity<String>("Product added successfully with id:" + count,HttpStatus.CREATED);
}
value = "product/controller/updateProduct",consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) (
public ResponseEntity<ProductBean> updateProduct( ProductBean product) {
if (productServiceImpl.getProductDetailsById(product.getId()) == null) {
ProductBean product2 = null;
return new ResponseEntity<ProductBean>(product2,HttpStatus.INTERNAL_SERVER_ERROR);
}
ProductBean updated = productServiceImpl.updateProduct(product);
return new ResponseEntity<ProductBean>(updated, HttpStatus.OK);
}
value = "product/controller/deleteProduct/{id}", produces = MediaType.APPLICATION_JSON_VALUE) (
public ResponseEntity<ProductBean> deleteProduct( ("id") int myId) {
ProductBean product2=productServiceImpl.getProductDetailsById(myId);
if (productServiceImpl.getProductDetailsById(myId) == null) {
return new ResponseEntity<ProductBean>(product2,HttpStatus.INTERNAL_SERVER_ERROR);
}
productServiceImpl.removeProduct(myId);
return new ResponseEntity<ProductBean>(product2, HttpStatus.OK);
}
}
5. Create ProductService Inteface in com.example.productservice.service package.
xxxxxxxxxx
package com.example.productservice.service;
import java.util.Collection;
import com.example.productservice.business.bean.ProductBean;
public interface ProductService {
Collection<ProductBean> getAllProduct();
ProductBean getProductDetailsById(int id);
Integer addProduct(ProductBean product);
ProductBean updateProduct(ProductBean product);
void removeProduct(int id);
}
6. Create ProductServiceImpl class in com.example.productservice.service package.
xxxxxxxxxx
package com.example.productservice.service;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.productservice.business.bean.ProductBean;
import com.example.productservice.dao.ProductDAOWrapper;
public class ProductServiceImpl implements ProductService {
private ProductDAOWrapper productDAOWrapper;
public Collection<ProductBean> getAllProduct(){
return productDAOWrapper.findAll();
}
public ProductBean getProductDetailsById(int id){
return productDAOWrapper.findOne(id);
}
public Integer addProduct(ProductBean product){
return productDAOWrapper.saveProduct(product);
}
public ProductBean updateProduct (ProductBean product){
return productDAOWrapper.updateProduct(product);
}
public void removeProduct (int id){
productDAOWrapper.delete(id);
}
}
7. Create ProductDAO interface in com.example.productservice.dao package.
xxxxxxxxxx
package com.example.productservice.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.productservice.entity.ProductEntity;
public interface ProductDAO extends JpaRepository<ProductEntity, Integer>
{
}
8. Create ProductDAOWrapper class in com.example.productservice.dao package.
xxxxxxxxxx
package com.example.productservice.dao;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.productservice.business.bean.ProductBean;
import com.example.productservice.entity.ProductEntity;
public class ProductDAOWrapper {
private ProductDAO dao;
public List<ProductBean>findAll(){
List<ProductBean> list = new ArrayList<ProductBean>();
Iterable<ProductEntity> listEn= dao.findAll();
listEn.forEach(x->{
ProductBean product = new ProductBean();
BeanUtils.copyProperties(x, product);
list.add(product);
});
return list;
}
public ProductBean findOne(Integer id){
ProductEntity x= dao.findById(id).get();
ProductBean product =null;
if(x!=null){
product = new ProductBean();
BeanUtils.copyProperties(x, product);
}
System.out.println(x);
return product;
}
public Integer saveProduct(ProductBean product){
ProductEntity productEntityBean = new ProductEntity();
BeanUtils.copyProperties(product, productEntityBean);
ProductEntity en= dao.save(productEntityBean);
return en.getId();
}
public ProductBean updateProduct(ProductBean product){
ProductEntity productEntityBean = new ProductEntity();
BeanUtils.copyProperties(product, productEntityBean);
ProductEntity en= dao.save(productEntityBean);
ProductBean product2 = new ProductBean();
BeanUtils.copyProperties(en,product2);
return product2;
}
public void delete(int id){
dao.deleteById(id);
}
}
9. Create ProductEntity Class in com.example.productservice.entity package.
xxxxxxxxxx
package com.example.productservice.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
name="product") (
public class ProductEntity {
strategy=GenerationType.IDENTITY) (
private int id;
private String name;
private String description;
private Double price;
// getters and setters
}
Run the application using clean install spring-boot:run in maven goal.
Open Browser and hit the Url as:
http://localhost:8080/emp/controller/getDetails
Create a Java Application with JDK 11 to consume the ProductService application created:
1. Add Jackson-annotations-2.7.3, Jackson-core-2.7.3, Jackson-databind-2.7.3 jar files in the module path.
2. Make the HttpClientAsyncDemo module as open.
xxxxxxxxxx
open module HttpClientAsyncDemo {
requires java.net.http;
requires jackson.databind;
requires jackson.core;
}
3. Create JSONUtils class in com.httpclientdemo.utility package. Used to convert JSON into Object and vice versa.
xxxxxxxxxx
package com.httpclientdemo.utility;
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JSONUtils {
//convert JSON into List of Objects
static public <T> List<T> convertFromJsonToList(String json, TypeReference<List<T>> var) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, var);
}
//Generic Type Safe Method – convert JSON into Object
static public <T> T covertFromJsonToObject(String json, Class<T> var) throws IOException{
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, var);//Convert Json into object of Specific Type
}
//convert Object into JSON
public static String covertFromObjectToJson(Object obj) throws JsonProcessingException{
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(obj);
}
}
4. Create ProductBean class in com.httpclientdemo.business.bean package as same as ProductService application.
5. Create a main java class HttpClientAsyncDemo class in com.httpclientdemo.uiclient package.
xxxxxxxxxx
package com.httpclientdemo.uiclient;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.httpclientdemo.business.bean.ProductBean;
import com.httpclientdemo.utility.JSONUtils;
public class HttpClientAsyncDemo {
private static final HttpClient client = HttpClient.newBuilder().version(Version.HTTP_2).build();
private static final String serviceURL = "http://localhost:8080/product/controller/";
public static void main(String[] args) throws InterruptedException, ExecutionException, IOException{
//getAllProducts();
//getProductDetailsById();
addProduct();
//updateProduct();
//deleteProduct();
}
//sending request to retrieve all the products available.
public static void getAllProducts() throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException
{
HttpRequest req = HttpRequest.newBuilder(URI.create(serviceURL+"getDetails"))
.GET().build();
CompletableFuture<HttpResponse<String>> response = client.sendAsync(req, BodyHandlers.ofString());
response.thenAccept(res -> System.out.println(res));
List<ProductBean> products = JSONUtils.convertFromJsonToList(response.get().body(), new TypeReference<List<ProductBean>>() {});
products.forEach(System.out::println);
response.join();
}
//sending request retrieve the product based on the productId
public static void getProductDetailsById() throws InterruptedException, ExecutionException, IOException
{
HttpRequest req = HttpRequest.newBuilder(URI.create(serviceURL+"getDetailsById/1"))
.GET().build();
CompletableFuture<HttpResponse<String>> response = client.sendAsync(req, BodyHandlers.ofString());
response.thenAccept(res -> System.out.println(res));
if(response.get().statusCode() == 500)
System.out.println("Product Not Avaialble");
else
{
ProductBean bean = JSONUtils.covertFromJsonToObject(response.get().body(), ProductBean.class);
System.out.println(bean);
}
response.join();
}
//send request to add the product details.
public static void addProduct() throws InterruptedException, ExecutionException, JsonProcessingException
{
ProductBean bean = new ProductBean();
bean.setName("handwash");
bean.setDescription("Herbal");
bean.setPrice(143.10);
String inputJson = JSONUtils.covertFromObjectToJson(bean);
HttpRequest request = HttpRequest.newBuilder(URI.create(serviceURL+"addProduct"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(inputJson)).build();
CompletableFuture<HttpResponse<String>> response = client.sendAsync(request,HttpResponse.BodyHandlers.ofString());
System.out.println(response.get().body());
}
//send request to update the product details.
public static void updateProduct() throws InterruptedException, ExecutionException, IOException
{
ProductBean bean = new ProductBean();
bean.setId(1);
bean.setName("Hand Wash");
bean.setDescription("Herbal Prodcuct by XYZ company");
bean.setPrice(198.10);
String inputJson=JSONUtils.covertFromObjectToJson(bean);
HttpRequest request = HttpRequest.newBuilder(URI.create(serviceURL+"updateProduct"))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(inputJson)).build();
CompletableFuture<HttpResponse<String>> response = client.sendAsync(request,HttpResponse.BodyHandlers.ofString());
if(response.get().statusCode() == 500)
System.out.println("Product Not Avaialble to update");
else {
bean = JSONUtils.covertFromJsonToObject(response.get().body(), ProductBean.class);
System.out.println(bean);
}
response.join();
}
//send request to delete the product by its productId
public static void deleteProduct() throws ExecutionException, InterruptedException, IOException
{
HttpRequest request = HttpRequest.newBuilder(URI.create(serviceURL+"deleteProduct/1"))
.DELETE().build();
CompletableFuture<HttpResponse<String>> response = client.sendAsync(request,HttpResponse.BodyHandlers.ofString());
if(response.get().statusCode() == 500)
System.out.println("Product Not Available to delete");
else {
ProductBean bean = JSONUtils.covertFromJsonToObject(response.get().body(), ProductBean.class);
System.out.println(bean);
}
response.join();
}
}
Run the application. You can observe Product details added in the database table.
Uncomment the getProductDetailsById() in the main method, comment the other methods and run the application you can observe the product details displayed in the console.
Likewise, you can comment and uncomment the functionalities provided in the main class and observe the results.
Conclusion
With the HttpClient client in Java 11, we can send different types of requests Synchronously/Asynchronously with support of HTTP/1.1 and HTTP 2. No need to add any external http client library to your project.
Opinions expressed by DZone contributors are their own.
Comments