Ví dụ Spring Boot Restful Client với RestTemplate

Xem thêm các chuyên mục:

1- Mục tiêu của ví dụ

Tài liệu được viết dựa trên:
  • Spring Boot 2.x

  • RestTemplate

  • Eclipse 3.7

Trong bài viết này tôi sẽ hướng dẫn bạn tạo một ứng dụng Restful Client sử dụng Spring Boot. Với 4 chức năng:
  1. Tạo một request với phương thức GET, gửi đến Restful Web Service để nhận lấy một danh sách các nhân viên (employee), hoặc thông tin một nhân viên. Dữ liệu nhận được có định dạng XML hoặc JSON.
  2. Tạo một request với phương thức PUT, gửi đến Restful Web Service để yêu cầu sửa đổi thông tin một nhân viên, dữ liệu đính kèm theo request có định dạng XML hoặc JSON.
  3. Tạo một request với phương thức POST, gửi đến Restful Web Service để yêu cầu thêm mới một nhân viên, dữ liệu đính kèm theo request có định dạng XML hoặc JSON.
  4. Tạo một request với phương thức DELETE, gửi đến Restful Web Service để yêu cầu xóa một nhân viên.
     
Bài viết này sử dụng Restful Web Service được tạo ra từ ví dụ dưới đây:
Lớp RestTemplate là một lớp trung tâm trong Spring Framework cho các cuộc gọi đồng bộ (synchronous calls) bởi Client để truy cập vào RESTful Web Service. Lớp này cung cấp các chức năng để tiêu thụ REST Services một cách dễ dàng. Khi sử dụng lớp nói trên, người dùng chỉ phải cung cấp URL, các tham số (nếu có) và trích xuất các kết quả nhận được. RestTemplate quản lý các kết nối HTTP (HTTP Connection).

2- Tạo Spring Boot project

Trên Eclipse, tạo Spring Boot project.
OK, project đã được tạo ra.

3- Cấu hình pom.xml

Project này cần sử dụng các thư viện Spring Restful Client. Vì vậy bạn có hai lựa chọn:
  1. spring-boot-starter-web
  2. spring-boot-starter-data-rest

spring-boot-starter-web

spring-boot-starter-web bao gồm các thư viện để xây dựng một ứng dụng web sử dụng Spring MVC, và sử dụng tomcat như là một Web Container mặc định được nhúng vào (embedded). Nó bao gồm cả các thư viện cho ứng dụng RESTful.
spring-boot-starter-web
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-data-rest

spring-boot-starter-data-rest bao gồm các thư viện để làm việc với Spring RESTful.
spring-boot-starter-data-rest
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-rest -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>            
</dependency>

Java <==> JSON

Cả hai "Starter" ở trên đều chứa thư viện jackson-databind hỗ trợ việc chuyển đổi một đối tượng Java thành JSON và ngược lại.

Java <==> XML

Spring sử dụng JAXB (Có sẵn trong JDK) để chuyển đổi đối tượng Java thành XML và ngược lại. Tuy nhiên các lớp Java phải được chú thích (annotate) bởi @XmlRootElement,... Vì vậy lời khuyên của tôi là bạn nên sử dụng jackson-dataformat-xml như là một thư viện để chuyển đổi XMLJava. Để sử dụng jackson-dataformat-xml bạn cần khai báo nó trong tập tin pom.xml:
jackson-dataformat-xml
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
OK, bạn có 2 lựa chọn để khai báo trong pom.xml:
** pom.xml (Option 1) **
<dependencies>
     ......

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>            
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
    
        .....
</dependencies>
** pom.xml (Option 2) **
<dependencies>
     ......

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>            
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
    
        .....
</dependencies>
Thư viện Apache Commons Codec cần thiết để mật mã hóa (encode) username/password trong trường hợp bạn sử dụng Rest Client để truy cập các nguồn dữ liệu được bảo mật bởi Basic Authentication.
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
</dependency>
Nội dung đầy đủ của tập tin pom.xml:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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
    http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.o7planning</groupId>
    <artifactId>SpringBootRestfulClient</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>SpringBootRestfulClient</name>
    <description>Spring Boot + Restful Client</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</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-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>            
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

4- GET - getForObject

Sử dụng phương thức getForObject để gửi một yêu cầu (request) tới Restful Service, và nhận được dữ liệu trả về. Dưới đây là ví dụ đơn giản nhất, dữ liệu trả về là một String.
SimplestGetExample.java
package org.o7planning.sbrestfulclient.get;

import org.springframework.web.client.RestTemplate;

public class SimplestGetExample {

	static final String URL_EMPLOYEES = "http://localhost:8080/employees";

	static final String URL_EMPLOYEES_XML = "http://localhost:8080/employees.xml";
	static final String URL_EMPLOYEES_JSON = "http://localhost:8080/employees.json";

	public static void main(String[] args) {

		RestTemplate restTemplate = new RestTemplate();

		// Gửi yêu cầu với phương thức GET và Headers mặc định.
		String result = restTemplate.getForObject(URL_EMPLOYEES, String.class);

		System.out.println(result);
	}

}
Các yêu cầu gửi đến Restful Service cần phải tùy biến thông tin Headers để nói với Restful Service kiểu định dạng dữ liệu mà bạn muốn nhận được ( JSON, XML, ...)
GetWithHeaderExample.java
package org.o7planning.sbrestfulclient.get;

import java.util.Arrays;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class GetWithHeaderExample {

	static final String URL_EMPLOYEES = "http://localhost:8080/employees";

	public static void main(String[] args) {

		// HttpHeaders
		HttpHeaders headers = new HttpHeaders();

		headers.setAccept(Arrays.asList(new MediaType[] { MediaType.APPLICATION_JSON }));
		// Yêu cầu trả về định dạng JSON
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.set("my_other_key", "my_other_value");

		// HttpEntity<String>: To get result as String.
		HttpEntity<String> entity = new HttpEntity<String>(headers);

		// RestTemplate
		RestTemplate restTemplate = new RestTemplate();

		// Gửi yêu cầu với phương thức GET, và các thông tin Headers.
		ResponseEntity<String> response = restTemplate.exchange(URL_EMPLOYEES, //
				HttpMethod.GET, entity, String.class);

		String result = response.getBody();

		System.out.println(result);
	}

}
Dữ liệu trả về từ Restful Serivce có định dạng XML hoặc JSON có thể tự động được chuyển đổi (Convert) thành đói tượng Java.
Employee.java
package org.o7planning.sbrestfulclient.model;

public class Employee {

    private String empNo;
    private String empName;
    private String position;

    public Employee() {

    }

    public Employee(String empNo, String empName, String position) {
        this.empNo = empNo;
        this.empName = empName;
        this.position = position;
    }

    public String getEmpNo() {
        return empNo;
    }

    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

}
SimplestGetPOJOExample.java
package org.o7planning.sbrestfulclient.get;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.web.client.RestTemplate;

public class SimplestGetPOJOExample {

	static final String URL_EMPLOYEES = "http://localhost:8080/employees";

	static final String URL_EMPLOYEES_XML = "http://localhost:8080/employees.xml";
	static final String URL_EMPLOYEES_JSON = "http://localhost:8080/employees.json";

	public static void main(String[] args) {

		RestTemplate restTemplate = new RestTemplate();

		// Gửi yêu cầu với phương thức GET và Headers mặc định.
		Employee[] list = restTemplate.getForObject(URL_EMPLOYEES, Employee[].class);

		if (list != null) {
			for (Employee e : list) {
				System.out.println("Employee: " + e.getEmpNo() + " - " + e.getEmpName());
			}
		}

	}

}

5- GET - exchange

Sử dụng phương thức exchange cũng giúp bạn có thể gửi một request tới Restful Service, kết quả trả về là đối tượng ResponseEntity, đối tượng này chứa nhiều thông tin đáng chú ý, chẳng hạn như HttpStatus,...
GetPOJOWithHeaderExample.java
package org.o7planning.sbrestfulclient.get;

import java.util.Arrays;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class GetPOJOWithHeaderExample {

	static final String URL_EMPLOYEES = "http://localhost:8080/employees";

	public static void main(String[] args) {

		// HttpHeaders
		HttpHeaders headers = new HttpHeaders();

		headers.setAccept(Arrays.asList(new MediaType[] { MediaType.APPLICATION_XML }));
		// Yêu cầu trả về định dạng XML
		headers.setContentType(MediaType.APPLICATION_XML);
		headers.set("my_other_key", "my_other_value");

		// HttpEntity<Employee[]>: To get result as Employee[].
		HttpEntity<Employee[]> entity = new HttpEntity<Employee[]>(headers);

		// RestTemplate
		RestTemplate restTemplate = new RestTemplate();

		// Gửi yêu cầu với phương thức GET, và các thông tin Headers.
		ResponseEntity<Employee[]> response = restTemplate.exchange(URL_EMPLOYEES, //
				HttpMethod.GET, entity, Employee[].class);

		HttpStatus statusCode = response.getStatusCode();
		System.out.println("Response Satus Code: " + statusCode);

		// Status Code: 200
		if (statusCode == HttpStatus.OK) {
			// Response Body Data
			Employee[] list = response.getBody();

			if (list != null) {
				for (Employee e : list) {
					System.out.println("Employee: " + e.getEmpNo() + " - " + e.getEmpName());
				}
			}
		}

	}

}

6- GET - Basic Authentication

Với các nguồn dữ liệu (resource) được bảo mật bởi Basic Authentication, các request (yêu cầu) mà bạn gửi tới REST Service phải đính kèm theo username/password. Thông tin username/password cần phải được mật mã hóa (encode) sử dụng thuật toán Base64 trước khi đính kèm theo request.
Xem thêm:
GetWithBasicAuthExample.java
package org.o7planning.sbrestfulclient.get;

import java.nio.charset.Charset;
import java.util.Arrays;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.apache.commons.codec.binary.Base64;

public class GetWithBasicAuthExample {

	public static final String USER_NAME = "tom";
	public static final String PASSWORD = "123";

	static final String URL_EMPLOYEES = "http://localhost:8080/employees";

	public static void main(String[] args) {

		// HttpHeaders
		HttpHeaders headers = new HttpHeaders();

		// 
		// Authentication
		// 
		String auth = USER_NAME + ":" + PASSWORD;
		byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.forName("US-ASCII")));
		String authHeader = "Basic " + new String(encodedAuth);
		headers.set("Authorization", authHeader);
		// 
		headers.setAccept(Arrays.asList(new MediaType[] { MediaType.APPLICATION_JSON }));
		// Yêu cầu trả về định dạng JSON
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.set("my_other_key", "my_other_value");

		// HttpEntity<String>: To get result as String.
		HttpEntity<String> entity = new HttpEntity<String>(headers);

		// RestTemplate
		RestTemplate restTemplate = new RestTemplate();

		// Gửi yêu cầu với phương thức GET, và các thông tin Headers.
		ResponseEntity<String> response = restTemplate.exchange(URL_EMPLOYEES, //
				HttpMethod.GET, entity, String.class);

		String result = response.getBody();

		System.out.println(result);
	}

}

7- POST - postForObject

Phương thức postForObject được sử dụng để gửi một request tới Restful Service để tạo một nguồn dữ liệu (resource), đồng thời trả về nguồn dữ liệu vừa được tạo.
Post_postForObject_Example.java
package org.o7planning.sbrestfulclient.post;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

public class Post_postForObject_Example {

	static final String URL_CREATE_EMPLOYEE = "http://localhost:8080/employee";

	public static void main(String[] args) {

		String empNo = "E11";

		Employee newEmployee = new Employee(empNo, "Tom", "Cleck");

		HttpHeaders headers = new HttpHeaders();
		headers.add("Accept", MediaType.APPLICATION_XML_VALUE);
		headers.setContentType(MediaType.APPLICATION_XML);

		RestTemplate restTemplate = new RestTemplate();

		// Dữ liệu đính kèm theo yêu cầu.
		HttpEntity<Employee> requestBody = new HttpEntity<>(newEmployee, headers);

		// Gửi yêu cầu với phương thức POST.
		Employee e = restTemplate.postForObject(URL_CREATE_EMPLOYEE, requestBody, Employee.class);

		if (e != null && e.getEmpNo() != null) {

			System.out.println("Employee created: " + e.getEmpNo());
		} else {
			System.out.println("Something error!");
		}
 
	}

}

8- POST - postForEntity

Phương thức postForEntity được sử dụng để gửi một request tới Restful Service để tạo một nguồn dữ liệu (resource). Phương thức này trả về đối tượng ResponseEntity, đối tượng này chứa nguồn dữ liệu vừa được tạo ra, và các thông tin đáng quan tâm khác, chẳng hạn như HttpStatus, ...
Post_postForEntity_Example.java
package org.o7planning.sbrestfulclient.post;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class Post_postForEntity_Example {

	static final String URL_CREATE_EMPLOYEE = "http://localhost:8080/employee";

	public static void main(String[] args) {

		Employee newEmployee = new Employee("E11", "Tom", "Cleck");

		RestTemplate restTemplate = new RestTemplate();

		// Dữ liệu đính kèm theo yêu cầu.
		HttpEntity<Employee> requestBody = new HttpEntity<>(newEmployee);

		// Gửi yêu cầu với phương thức POST.
		ResponseEntity<Employee> result 
		     = restTemplate.postForEntity(URL_CREATE_EMPLOYEE, requestBody, Employee.class);

		System.out.println("Status code:" + result.getStatusCode());

		// Code = 200.
		if (result.getStatusCode() == HttpStatus.OK) {
			Employee e = result.getBody();
			System.out.println("(Client Side) Employee Created: "+ e.getEmpNo());
		}

	}

}

9- PUT - Ví dụ đơn giản

Phương thức put của lớp RestTemplate được sử dụng để gửi một request tới Restful Service để thay đổi một nguồn dữ liệu (resource). Phương thức này không trả về gì cả.
PutSimpleExample.java
package org.o7planning.sbrestfulclient.put;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

public class PutSimpleExample {

	static final String URL_UPDATE_EMPLOYEE = "http://localhost:8080/employee";
	static final String URL_EMPLOYEE_PREFIX = "http://localhost:8080/employee";

	public static void main(String[] args) {

		String empNo = "E01";

		Employee updateInfo = new Employee(empNo, "Tom", "Cleck");

		HttpHeaders headers = new HttpHeaders();
		headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);

		RestTemplate restTemplate = new RestTemplate();

		// Dữ liệu đính kèm theo yêu cầu.
		HttpEntity<Employee> requestBody = new HttpEntity<>(updateInfo, headers);

		// Gửi yêu cầu với phương thức PUT.
		restTemplate.put(URL_UPDATE_EMPLOYEE, requestBody, new Object[] {});

		String resourceUrl = URL_EMPLOYEE_PREFIX + "/" + empNo;

		Employee e = restTemplate.getForObject(resourceUrl, Employee.class);

		if (e != null) {
			System.out.println("(Client side) Employee after update: ");
			System.out.println("Employee: " + e.getEmpNo() + " - " + e.getEmpName());
		}
	}

}

10- PUT - exchange

Ví dụ sử dụng phương thức exchange của lớp RestTemplate để gửi một request tới Restful Service để thay đổi một nguồn dữ liệu.
PutWithExchangeExample.java
package org.o7planning.sbrestfulclient.put;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

public class PutWithExchangeExample {

	static final String URL_UPDATE_EMPLOYEE = "http://localhost:8080/employee";
	static final String URL_EMPLOYEE_PREFIX = "http://localhost:8080/employee";

	public static void main(String[] args) {

		String empNo = "E01";

		Employee updateInfo = new Employee(empNo, "Tom", "Cleck");

		HttpHeaders headers = new HttpHeaders();
		headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);

		RestTemplate restTemplate = new RestTemplate();

		// Dữ liệu đính kèm theo yêu cầu.
		HttpEntity<Employee> requestBody = new HttpEntity<>(updateInfo, headers);

		// Gửi yêu cầu với phương thức PUT.
		restTemplate.exchange(URL_UPDATE_EMPLOYEE, HttpMethod.PUT, requestBody, Void.class);

		String resourceUrl = URL_EMPLOYEE_PREFIX + "/" + empNo;

		Employee e = restTemplate.getForObject(resourceUrl, Employee.class);

		if (e != null) {
			System.out.println("(Client side) Employee after update: ");
			System.out.println("Employee: " + e.getEmpNo() + " - " + e.getEmpName());
		}
	}

}

11- DELELE

Sử dụng phương thức delete của lớp RestTemplate để gửi một request tới Restful Service để xóa một nguồn dữ liệu.
DeleteSimpleExample.java
package org.o7planning.sbrestfulclient.delete;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.web.client.RestTemplate;

public class DeleteSimpleExample {

	public static void main(String[] args) {

		RestTemplate restTemplate = new RestTemplate();

		// empNo="E01"
		String resourceUrl = "http://localhost:8080/employee/E01";

		// Gửi yêu cầu với phương thức DELETE.
		restTemplate.delete(resourceUrl);

		// Get 
		Employee e = restTemplate.getForObject(resourceUrl, Employee.class);

		if (e != null) {
			System.out.println("(Client side) Employee after delete: ");
			System.out.println("Employee: " + e.getEmpNo() + " - " + e.getEmpName());
		} else {
			System.out.println("Employee not found!");
		}
	}

}
DeleteExample2.java
package org.o7planning.sbrestfulclient.delete;

import org.o7planning.sbrestfulclient.model.Employee;
import org.springframework.web.client.RestTemplate;

public class DeleteExample2 {

	public static void main(String[] args) {

		RestTemplate restTemplate = new RestTemplate();

		// URL với biến URI (URI variable)
		String resourceUrl = "http://localhost:8080/employee/{empNo}";

		Object[] uriValues = new Object[] { "E01" }; 

		// Gửi yêu cầu với phương thức DELETE.
		restTemplate.delete(resourceUrl, uriValues);

		Employee e = restTemplate.getForObject(resourceUrl, Employee.class);

		if (e != null) {
			System.out.println("(Client side) Employee after delete: ");
			System.out.println("Employee: " + e.getEmpNo() + " - " + e.getEmpName());
		} else {
			System.out.println("Employee not found!");
		}
	}

}

Xem thêm các chuyên mục: