preface
In the field of Java server development, Spring is an inseparable topic, especially now the concept of microservices is popular, the emergence of Spring Boot has injected new vitality to Spring, in addition to Spring Cloud, these frameworks make Spring technology system more rich. Spring has iterated from 1.0.0 in 2014 to 5.2.0 M1, introducing new features and functionality as the Java language evolves. This article focuses on the RestTemplate content of the Spring framework to reduce the dependency on the HttpClient API that we often use for development. See Resttemplate Demo at Github. Thank you for your careful proofreading. If there are any technical problems or flaws in the article, please leave a message and contact us at 😁.
knowRestTemplate
Before we learn to use RestTemplate, let’s take a look at this class and see how Spring officially describes it. This class can be described in the official API document RestTemplate Javadoc:
Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others. The RestTemplate offers templates for common scenarios by HTTP method, in addition to the generalized exchange and execute methods that support of less frequent cases.
It is clear from here that RestTemplate performs HTTP requests synchronously, with the underlying use of the JDK native HttpURLConnection API, or other HTTP client request libraries such as HttpComponents. It is also emphasized that RestTemplate provides a templating method to make it easier for developers to send HTTP requests.
It is worth noting that the RestTemplate class was introduced in Spring Framework 3.0, and we are using the current GA version 5.1.6 of Spring. In 5.0 above, the official noted the more it is recommended to use non-blocking HTTP response type the request processing class org. Springframework. Web. Reactive. Client. The WebClient to replace the RestTemplate, This is especially true for asynchronous request processing scenarios.
Here’s a quick summary of what RestTemplate is: RestTemplate is the class that Spring encapsulates to handle synchronous HTTP requests. Specific how to use this class for HTTP request operations, visible in the actual part of the article.
RestTemplate provides nearly 30 request methods, most of which are single-method overloading implementations. Here, I refer to the official reST-client-access document for the following categories:
The method name | describe |
---|---|
getForObject |
The response is obtained through a GET request |
getForEntity |
Obtain it through the GET requestResponseEntity Object containing the status code, response header, and response data |
headForHeaders |
The HEAD request resource returns all the response headers |
postForLocation |
Create a resource with a POST request and return the fields of the response header in the response dataLocation The data of |
postForObject |
A resource is created through a POST request and the response results are obtained |
put |
Create or update a resource using a PUT request |
patchForObject |
The resource is updated through a PATH request and the response result is obtained. (JDKHttpURLConnection Do not support PATH request, other HTTP client libraries support) |
delete |
DELETE resources in DELETE mode |
optionsForAllow |
To obtain all the HTTP methods that a resource is allowed to access by requesting in ALLOW mode, you can see which request modes are supported by a request |
exchange |
The more general version of the request handling method accepts oneRequestEntity Object, you can set the path, request header, request information, etc., and finally return oneResponseEntity entity |
execute |
The most common method of executing HTTP requests, all of which are based onexecute To fully control the request information and obtain the response data through the callback interface |
It’s hard to remember all the methods I’ve seen, but to understand it better, take a quick look at the class hierarchy of RestTemplate, which you can see in the official source code:
/**
* Interface specifying a basic set of RESTful operations.
* Implemented by {@link RestTemplate}. Not often used directly, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
* @see RestTemplate
*/
public interface RestOperations {... }Copy the code
The RestTemplate request methods are all derived from the RestOperations interface, which provides RESTful request operations such as GET, POST, PUT, DELETE, and so on. For details, see RestOperation Javadoc.
About RESTful:
From Wikipedia: Presentation Layer State Transition, a style of software building designed to provide worldwide web services, also known as REST for short.
Use URLS to locate resources, use HTTP verbs to describe operations, such as GET,POST,DELETE, and PUT. Simply use URLS to know what resources to access, and HTTP methods to know what operations to perform. The HTTP Status Code is used to know the result of the execution.
In actual combatRestTemplate
Now that you’ve seen the RestTemplate class briefly, let’s try it out and see how it works.
1. Generate a Demo project and import the IDE
In order to quickly build a Demo, we use the Spring Boot framework to build. First, we use the official Spring Initializr to generate a quick build project skeleton. Select The Spring Boot version 2.1.4 and the Spring Framework version of its underlying dependency is the latest release version 5.1.6. Select only one Web module for THE POM dependency to facilitate the establishment of Web applications.
Click the “generate project” button, you can download the project compression package, decompress and import the project with your usual IDE, the project structure is as follows:
Project ResttemplateApplication. Java bootstrap class for the entire program, used to start the project.
Write the request controller class ProductController
First, in order to be able to send multiple HTTP requests using the RestTemplate, build the product controller that accepts HTTP requests locally. New package com. One. Learn. Resttemplate. Controller, new product controller ProductController, code is as follows:
@RequestMapping("/product")
@RestController
public class ProductController {
@GetMapping("/get_product1")
public Product get_product1(a) {
return new Product(1."ProductA", BigDecimal.valueOf(6666.0));
}
@GetMapping("/get_product2")
public Product get_product2(Integer id) {
return new Product(id, "ProductC", BigDecimal.valueOf(6666.0));
}
@GetMapping("/get_product3")
public String get_product3(Product product) {
return product.toString();
}
@PostMapping("/post_product1")
public String post_product1(Product product) {
return product.toString();
}
@PostMapping("/post_product2")
public String post_product2(@RequestBody Product product) {
return product.toString();
}
@DeleteMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
String result = String.format("Product with number %s deleted successfully", id);
System.out.println(result);
return result;
}
@PutMapping("/update")
public String updateByPut(Product product) {
String result = product.toString() + "Update successful";
System.out.println(result);
return result;
}
@PostMapping("/upload")
public String upload(MultipartRequest request) {
// Spring MVC uses MultipartRequest to accept HTTP requests with files
MultipartFile file = request.getFile("file");
String originalFilename = file.getOriginalFilename();
return "upload success filename: "+ originalFilename; }}Copy the code
Involved in the Product controller entity class Product created in com. One. Learn. The resttemplate. Bean bag, the code is as follows:
public class Product {
private Integer id;
private String name;
private BigDecimal price;
public Product(a) {}public Product(Integer id, String name, BigDecimal price) {
this.id = id;
this.name = name;
this.price = price;
}
// Omit the setter and getter methods
@Override
public String toString(a) {
return "Product{" +
"id='" + id + '\' ' +
", name='" + name + '\' ' +
", price='" + price + '\' ' +
'} '; }}Copy the code
With these classes, you can use the application Boot class ResttemplateApplication to start the Spring Boot project. You have a simple Web application listening on port 8080. The result is as follows:
We can test the simple, open a browser, visit http://localhost:8080/product/get_product1, will see the results as shown:
3. Write a test class to send HTTP requests with the RestTemplate
Now that you have the Web service, it’s time to send the request and process the response using the RestTemplate. We in the test file under a new test class. Com one. Learn. Resttemplate. RestTemplateTests, code is as follows:
public class RestTemplateTests {
RestTemplate restTemplate = null;
@Before
public void setup(a) {
restTemplate = newRestTemplate(); }}Copy the code
Here we write test methods to implement requests to the various interfaces of the Product controller using the RestTemplate API.
A GET request
Let’s try RestTemplate to access a GET request with path product/get_product1 and no parameters as follows:
@Test
public void testGet_product1(a) {
String url = "http://localhost:8080/product/get_product1";
// Method 1: Obtain JSON string data in GET mode
String result = restTemplate.getForObject(url, String.class);
System.out.println("Get_product1 returns:" + result);
Assert.hasText(result, "Get_product1 returns null");
// Method 2: GET to obtain the Product entity object after the JSON data mapping
Product product = restTemplate.getForObject(url, Product.class);
System.out.println("Get_product1 returns:" + product);
Assert.notNull(product, "Get_product1 returns null");
// getBody(); // getBody(); // getBody()
ResponseEntity<Product> responseEntity = restTemplate.getForEntity(url, Product.class);
System.out.println("Get_product1 returns:" + responseEntity);
Assert.isTrue(responseEntity.getStatusCode().equals(HttpStatus.OK), "Get_product1 response failed");
}
Copy the code
First look at the console output log after running the test method testGet_product1:
. Get_product1 returns: {"id": 1,"name":"ProductA"."price": 6666.0}... Get_product1 Returns Product{id='1', name='ProductA', price='6666.0'}... Get_product1 Returns: <200,Product{id='1', name='ProductA', price='6666.0'},[Content-Type:"application/json; charset=UTF-8", Transfer-Encoding:"chunked", Date:"Thu, 09 May 2019 15:37:25 GMT"]>
...
Copy the code
As you can see, the testGet_product1 request responded successfully and got the data, which is not very simple from the above code. Now for a slightly more complex request, use the Exchange and Execute methods of the RestTemplate API to send GET requests. You can control the request behavior in a more granular way, such as the Header information. TestGet_product1 = testGet_product1 = testGet_product1
@Test
public void testGet_product1(a) {
String url = "http://localhost:8080/product/get_product1";
//....
// Method 1: Build the request entity HttpEntity object, which is used to configure the Header information and request parameters
MultiValueMap header = new LinkedMultiValueMap();
header.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
HttpEntity<Object> requestEntity = new HttpEntity<>(header);
// Method 2: Perform the request to get the ResponseEntity object containing the Product entity object, using getBody()
ResponseEntity<Product> exchangeResult = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Product.class);
System.out.println("Get_product1 returns:" + exchangeResult);
Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "Get_product1 response failed");
// Method 3: Set the Header information according to the RequestCallback interface implementation class, and read the response data using the ResponseExtractor interface implementation class
String executeResult = restTemplate.execute(url, HttpMethod.GET, request -> {
request.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
}, (clientHttpResponse) -> {
InputStream body = clientHttpResponse.getBody();
byte[] bytes = new byte[body.available()];
body.read(bytes);
return new String(bytes);
}); // Note: This uses the Java8 feature: Lambda expression syntax. If you do not touch Lambda expressions, you can use anonymous inner classes instead
System.out.println("Get_product1 returns:" + executeResult);
Assert.hasText(executeResult, "Get_product1 returns null");
}
Copy the code
Run the test method testGet_product1 again.
. Get_product1 Returns: <200,Product{id='1', name='ProductA', price='6666.0'},[Content-Type:"application/json; charset=UTF-8", Transfer-Encoding:"chunked", Date:"Thu, 09 May 2019 16:00:22 GMT"] >... Get_product1 returns: {"id": 1,"name":"ProductA"."price":6666.0}
...
Copy the code
The results are returned normally, indicating that the request was executed correctly.
Now try to execute the GET request with parameters. Write a new test method in the same way.
@Test
public void testGet_product2(a) {
String url = "http://localhost:8080/product/get_product2/id={id}";
// Method 1: Add the parameter values to the variable length parameters and match the parameters in sequence
ResponseEntity<Product> responseEntity = restTemplate.getForEntity(url, Product.class, 101);
System.out.println(responseEntity);
Assert.isTrue(responseEntity.getStatusCode().equals(HttpStatus.OK), "Get_product2 request failed");
Assert.notNull(responseEntity.getBody().getId(), "Get_product2 failed to pass parameter");
// Method 2: Store the request parameters in the form of key and value pairs in the Map set, which is used for URL concatenation during the request
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("id".101);
Product result = restTemplate.getForObject(url, Product.class, uriVariables);
System.out.println(result);
Assert.notNull(result.getId(), "Get_product2 failed to pass parameter");
}
Copy the code
The normal operation results are as follows:
. <200,Product{id='101', name='ProductC', price='6666.0'},[Content-Type:"application/json; charset=UTF-8", Transfer-Encoding:"chunked", Date:"Fri, 10 May 2019 14:53:41 GMT"]>
...
Product{id='101', name='ProductC', price='6666.0'}...Copy the code
A POST request
Now that you’ve seen how to use the RestTemplate API to send GET requests, take a look at how to use the common POST request. Since the content-type of POST request data is different, more POST requests are sent. Here we take the two commonly used content types, Application/X-www-form-urlencoded and Application/JSON, as examples.
-
Send a POST request with the content-type of application/ X-www-form-urlencoded:
@Test public void testPost_product1(a) { String url = "http://localhost:8080/product/post_product1"; Product product = new Product(201."Macbook", BigDecimal.valueOf(10000)); // Set the content-type of the request to Application /x-www-form-urlencoded MultiValueMap<String, String> header = new LinkedMultiValueMap(); header.add(HttpHeaders.CONTENT_TYPE, (MediaType.APPLICATION_FORM_URLENCODED_VALUE)); // Mode 2: Join the request parameter values in the form of K=V with & to send the request String productStr = "id=" + product.getId() + "&name=" + product.getName() + "&price=" + product.getPrice(); HttpEntity<String> request = new HttpEntity<>(productStr, header); ResponseEntity<String> exchangeResult = restTemplate.exchange(url, HttpMethod.POST, request, String.class); System.out.println("post_product1: " + exchangeResult); Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "Post_product1 request not successful"); // Method 1: Store the request parameters as key and value pairs in the MultiValueMap collection for use when sending the request MultiValueMap<String, Object> map = new LinkedMultiValueMap(); map.add("id", (product.getId())); map.add("name", (product.getName())); map.add("price", (product.getPrice())); HttpEntity<MultiValueMap> request2 = new HttpEntity<>(map, header); ResponseEntity<String> exchangeResult2 = restTemplate.exchange(url, HttpMethod.POST, request2, String.class); System.out.println("Post_product1." + exchangeResult2); Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "Post_product1 request not successful"); } Copy the code
The corresponding output logs are as follows:
. post_product1: <200,Product{id='201', name='Macbook', price='10000'},[Content-Type:"text/plain; charset=UTF-8", Content-Length:"48", Date:"Fri, 10 May 2019 16:07:43 GMT"] >... Post_product1: < 200, Product {id ='201', name='Macbook', price='10000'},[Content-Type:"text/plain; charset=UTF-8", Content-Length:"48", Date:"Fri, 10 May 2019 16:07:43 GMT"] >Copy the code
-
Send a POST request with content-type: application/json
@Test public void testPost_product2(a) { String url = "http://localhost:8080/product/post_product2"; // Set the content-type of the request to application/json MultiValueMap<String, String> header = new LinkedMultiValueMap(); header.put(HttpHeaders.CONTENT_TYPE, Arrays.asList(MediaType.APPLICATION_JSON_VALUE)); // Set Accept to indicate to the server what type of content the client can handle header.put(HttpHeaders.ACCEPT, Arrays.asList(MediaType.APPLICATION_JSON_VALUE)); // Pass the entity Product as the request parameter directly, and the underlying layer uses Jackson framework to serialize the request into A JSON string HttpEntity<Product> request = new HttpEntity<>(new Product(2."Macbook", BigDecimal.valueOf(10000)), header); ResponseEntity<String> exchangeResult = restTemplate.exchange(url, HttpMethod.POST, request, String.class); System.out.println("post_product2: " + exchangeResult); Assert.isTrue(exchangeResult.getStatusCode().equals(HttpStatus.OK), "Post_product2 request not successful"); } Copy the code
The output logs are as follows:
... post_product2: < 200, Product {id ='2', name='Macbook', price='10000'},[Content-Type:"application/json; charset=UTF-8", Content-Length:"46", Date:"Fri, 10 May 2019 16:09:11 GMT"] >...Copy the code
DELETE requests and PUT requests
DELETE request and PUT request are two types of RESTful request, but they are not usually used. Here is just a simple demonstration. The specific code is as follows:
// The DELETE method requests
@Test
public void testDelete(a) {
String url = "http://localhost:8080/product/delete/{id}";
restTemplate.delete(url, 101);
}
// The PUT method requests
@Test
public void testPut(a) {
String url = "http://localhost:8080/product/update"; Map<String, ? > variables =new HashMap<>();
MultiValueMap<String, String> header = new LinkedMultiValueMap();
header.put(HttpHeaders.CONTENT_TYPE, Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED_VALUE));
Product product = new Product(101."iWatch", BigDecimal.valueOf(2333));
String productStr = "id=" + product.getId() + "&name=" + product.getName() + "&price=" + product.getPrice();
HttpEntity<String> request = new HttpEntity<>(productStr, header);
restTemplate.put(url, request);
}
Copy the code
Upload a file
Now let’s try uploading files using the RestTemplate API again. It’s easy. First, let’s look at the implementation code:
@Test
public void testUploadFile(a) {
String url = "http://localhost:8080/product/upload";
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
FileSystemResource file = new FileSystemResource(new File("/Users/One/Desktop/b.txt"));
body.add("file", file);
MultiValueMap<String, String> header = new LinkedMultiValueMap();
header.put(HttpHeaders.CONTENT_TYPE, Arrays.asList(MediaType.MULTIPART_FORM_DATA_VALUE));
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, header);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
System.out.println("upload: " + responseEntity);
Assert.isTrue(responseEntity.getStatusCode().equals(HttpStatus.OK), "Upload request failed");
}
Copy the code
If the file Type data needs to be uploaded, only POST requests can be used and the Content Type is Multipart /form-data. You need to manually specify the content-type for the Header. The file to be uploaded can be encapsulated by FileSystemResource object, which represents a file resource. At the same time, the server needs to use MultipartRequest object to obtain file data. With the Web service already running, running the above test method yields the following log output:
. upload: <200,upload success filename: b.txt,[Content-Type:"text/plain; charset=UTF-8", Content-Length:"30", Date:"Fri, 10 May 2019 17:00:45 GMT"]>
...
Copy the code
Advanced RestTemplate
Now that you’ve learned several common ways the RestTemplate API requests data, let’s dig a little deeper into using RestTemplate.
Underlying HTTP request library switchover
Let’s start with the description of the official document:
The default constructor uses java.net.HttpURLConnection to perform requests. You can switch to a different HTTP library with an implementation of ClientHttpRequestFactory. There is built-in support for the following:
- Apache HttpComponents
- Netty
- OkHttp
Can be seen from the above RestTemplate using JDK native java.net.HttpURLConnection executing requests by default. In addition, Spring also encapsulates Apache HttpComponents, Netty, and OkHttp request libraries. The first one is the library associated with the HttpClient API. While Netty is a high performance NIO request processing network library, OkHttp for rich and efficient network framework, mostly used for Android programs.
The RestTemplate instance we created above uses the default constructor method, which uses the JDK’s native network API. To switch, simply pass the specific ClientHttpRequestFactory implementation class in the constructor, as follows:
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
Copy the code
For RestTemplate, the default HttpAccessor API uses JDK HttpURLConnection. For RestTemplate, the default HttpAccessor API uses JDK HttpURLConnection. For RestTemplate, the default HttpAccessor API uses JDK HttpURLConnection.
public abstract class HttpAccessor {
// ...
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// ...
}
Copy the code
But the Spring factory class SimpleClientHttpRequestFactory described as: Implementation that uses Standard JDK Facilities means that the default behavior of constructing a RestTemplate instance will use the JDK network API directly.
Request timeout setting
HttpURLConnection (HttpURLConnection, HttpURLConnection, HttpURLConnection, HttpURLConnection, HttpURLConnection) From SimpleClientHttpRequestFactory source class can see there is no timeout limit, also means that the infinite waiting for request and response:
// RestTemplate Default timeout setting.private int connectTimeout = -1;
private int readTimeout = -1; .Copy the code
How to adjust the timeout time, refer to the following code:
RestTemplate customRestTemplate = new RestTemplate(getClientHttpRequestFactory());
private SimpleClientHttpRequestFactory getClientHttpRequestFactory() { SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory(); / / connection timeout 10 s clientHttpRequestFactory. SetConnectTimeout (10 _000); / / read timeout 10 s clientHttpRequestFactory. SetReadTimeout _000 (10);return clientHttpRequestFactory;
}
Copy the code
To adjust the timeout setting of HttpComponentsClient, see the resttemplate-timeout-example article. Of course, there are many more parameters to customize than setting the timeout, and I will not list them here. See resttemplate-Httpclient-java-config for further information.
This brings us to the end of our study of RestTemplate, and if you are interested, you can explore the related source code further, and have a chance to try it out. 😁
The resources
www.baeldung.com/rest-templa…
Blog.didispace.com/spring-boot…
www.baeldung.com/spring-rest…
www.zhihu.com/question/28…
Howtodoinjava.com/spring-boot…
Docs. Spring. IO/spring/docs…
zh.wikipedia.org/wiki/ The status of the presentation layer changes to…
Docs. Spring. IO/spring – fram…