This article is for the blogger to study notes, the content comes from the Internet, if there is infringement, please contact delete.

Personal Note: github.com/dbses/TechN…

01 | build a RESTful style Web services

Understand RESTful architectural styles

REST (Representational State Transfer) is an architectural style that treats an access point on the server side as a resource and uses standard HTTP methods over transport protocols, such as the most common GET, PUT, POST, and DELETE.

The following table shows some concrete examples of RESTful styles:

There are also industry conventions for RESTful design of HTTP endpoints. In the case of the Account domain entity, if we think of it as a resource, the root node of the HTTP endpoint is usually named in the plural form, “/accounts.”

When designing RESTful apis, the following figure shows some of the differences between RESTful and non-restful apis for common CRUD operations.

Use basic annotations

  • @RestController

The @RestController annotation, inherited from the @Controller annotation in Spring MVC, is a RESTful HTTP endpoint and automatically serializes/deserializes HTTP requests and responses using JSON.

  • @GetMapping

The @getMapping annotation is defined very similarly to @RequestMapping, except that by default requestMethod.get specifies the HTTP method.

Controls the input of the request

  • @PathVariable

The @pathVariable annotation is used to retrieve path parameters. Get the value of the {id} parameter from a path of the form url/{id}.

  • @RequestParam

The @requestParam annotation is also used to get the parameters in the request, but it’s geared towards things like urls? The path id=XXX.

  • @RequestMapping

In HTTP, the Content-type attribute is used to specify what type of content is transferred. We can set this attribute in the @requestMapping annotation in Produces.

  • @RequestBody

The @requestbody annotation is used to handle encoded content whose content-type is application/json. The @requestbody annotation binds the json string in the RequestBody to the corresponding javabeans.

02 | how to use RestTemplate consumption RESTful services?

Create the RestTemplate

If we want to create a RestTemplate object, the simplest and most common way is to simply new an instance of the class, as shown in the following code:

@Bean
public RestTemplate restTemplate(a){
    return new RestTemplate();
}
Copy the code

Let’s look at the no-argument constructor for RestTemplate, as shown in the following code:

public RestTemplate(a) {
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    this.messageConverters.add(new StringHttpMessageConverter());
    this.messageConverters.add(new ResourceHttpMessageConverter(false));
    this.messageConverters.add(new SourceHttpMessageConverter<>());
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
 
    // Omit other code to add HttpMessageConverter
}
Copy the code

The no-argument constructor of RestTemplate adds a number of HttpMessageConverter objects for message transformation.

RestTemplate has another, more powerful, parameter constructor, as shown in the following code:

public RestTemplate(ClientHttpRequestFactory requestFactory) {
    this(a); setRequestFactory(requestFactory); }Copy the code

Can be used to set HTTP request timeout and other properties, as shown in the following code:

@Bean
public RestTemplate customRestTemplate(a){
    HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
    // Connection request timeout
    httpRequestFactory.setConnectionRequestTimeout(3000);
    // Connection timeout time
    httpRequestFactory.setConnectTimeout(3000);
    httpRequestFactory.setReadTimeout(3000);

    return new RestTemplate(httpRequestFactory);
}
Copy the code

Use RestTemplate to access the Web service

  • GET
public <T> T getForObject(URI url, Class<T> responseType)
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
public <T> T getForObject(String url, Class
       
         responseType, Map
        
          uriVariables)
        ,>
       
Copy the code

You can also use the getForEntity method to return a ResponseEntity object.

  • POST
Order order = new Order();
order.setOrderNumber("Order0001");
order.setDeliveryAddress("DemoAddress");
ResponseEntity<Order> responseEntity = restTemplate.postForEntity("http://localhost:8082/orders", order, Order.class);
return responseEntity.getBody();
Copy the code

PostForObject operates similarly.

  • exchange
ResponseEntity<Order> result = restTemplate.exchange("http://localhost:8082/orders/{orderNumber}", HttpMethod.GET, null, Order.class, orderNumber);
Copy the code
// Set the HTTP Header
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
 
// Set access parameters
HashMap<String, Object> params = new HashMap<>();
params.put("orderNumber", orderNumber);
 
// Set the request Entity
HttpEntity entity = new HttpEntity<>(params, headers);
ResponseEntity<Order> result = restTemplate.exchange(url, HttpMethod.GET, entity, Order.class);
Copy the code

RestTemplate other usage tips

  • Specify message converter

If we want to load the Gson-enabled GsonHttpMessageConverter into the RestTemplate, we can use the code shown below.

@Bean
public RestTemplate restTemplate(a) { List<HttpMessageConverter<? >> messageConverters =newArrayList<HttpMessageConverter<? > > (); messageConverters.add(new GsonHttpMessageConverter());
    RestTemplate restTemplate = new RestTemplate(messageConverters);
    return restTemplate;
}
Copy the code
  • Setting up interceptors

The most typical application scenario for this is to add load balancing to the RestTemplate in the Spring Cloud via the @loadBalanced annotation. We can find the following code in the LoadBalanceAutoConfiguration automatic configuration class.

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
    final LoadBalancerInterceptor loadBalancerInterceptor) {
    return new RestTemplateCustomizer() {
        @Override
        public void customize(RestTemplate restTemplate) {
            List<ClientHttpRequestInterceptor> list = newArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }}; }Copy the code
  • Handle exceptions

When the request status code does not return 200, the RestTemplate by default throws an exception and interrupts the rest of the operation. If we do not want to do this, we need to override the default ResponseErrorHandler. The sample code structure is as follows:

RestTemplate restTemplate = new RestTemplate();
 
ResponseErrorHandler responseErrorHandler = new ResponseErrorHandler() {
    @Override
    public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
        return true;
    }

    @Override
    public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
        // Add custom exception handling code}}; restTemplate.setErrorHandler(responseErrorHandler);Copy the code

03 | RestTemplate remote calls the principle

The class layer structure of RestTemplate looks like this:

The class layer structure is clearly divided into two branches, with the left branch providing implementation mechanisms related to HTTP requests, and the right branch providing RESTful entry points using object-oriented interfaces and abstract classes to achieve the convergence of these two parts of functionality.

InterceptingHttpAccessor

It is an abstract class that contains core variables as shown in the following code:

public abstract class InterceptingHttpAccessor extends HttpAccessor {
 
    private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
 
    private volatileClientHttpRequestFactory interceptingRequestFactory; ... }Copy the code

Interceptors are responsible for setting up and managing request interceptors; InterceptingRequestFactory responsible for creating the HTTP client requests.

InterceptingHttpAccessor also has a parent class HttpAccessor:

public abstract class HttpAccessor {
 
    private ClientHttpRequestFactory requestFactory = newSimpleClientHttpRequestFactory(); ... }Copy the code

Create SimpleClientHttpRequestFactory HttpAccessor as ClientHttpRequestFactory system default.

RestOperations

The RestOperations interface is defined as follows:

public interface RestOperations {
 
    <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;
    <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException;

    <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,Object... uriVariables) throws RestClientException;
 
    void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException;
	 
    void delete(String url, Object... uriVariables) throws RestClientException;
    <T> ResponseEntity<T> exchange(String url, HttpMethod method, @NullableHttpEntity<? > requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException; ... }Copy the code

RestTemplate core execution process

A good place to start is with an Exchange method with multiple requests, defined as follows:

@Override
public <T> ResponseEntity<T> exchange(String url, 
                                      HttpMethod method,
                                      @NullableHttpEntity<? > requestEntity, Class<T> responseType, Object... uriVariables)
    throws RestClientException {

    // Build request callback
    RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
    // Build the response body extractor
    ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    // Perform the remote call
    return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
}
Copy the code

The execute method is defined as follows:

@Override
@Nullable
public <T> T execute(String url, 
                     HttpMethod method, 
                     @Nullable RequestCallback requestCallback, 
                     @Nullable ResponseExtractor<T> responseExtractor, 
                     Object... uriVariables) throws RestClientException {
    URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    return doExecute(expanded, method, requestCallback, responseExtractor);
}
Copy the code

The doExecute method is defined as follows:

protected <T> T doExecute(URI url, 
                          @Nullable HttpMethod method, 
                          @Nullable RequestCallback requestCallback,
                          @Nullable ResponseExtractor<T> responseExtractor) 
    throws RestClientException {
    Assert.notNull(url, "URI is required");
    Assert.notNull(method, "HttpMethod is required");
    ClientHttpResponse response = null;
    try {
        // 1. Create request object
        ClientHttpRequest request = createRequest(url, method);
        if(requestCallback ! =null) {
            // Perform a callback to the request
            requestCallback.doWithRequest(request);
        }
      
        // 2. Get the result of the call
        response = request.execute();
        
        // 3. Process the result of the call
        handleResponse(url, method, response);
        // Extract data from results
        return(responseExtractor ! =null ? responseExtractor.extractData(response) : null);
    } catch(IOException ex) { String resource = url.toString(); String query = url.getRawQuery(); resource = (query ! =null ? resource.substring(0, resource.indexOf('? ')) : resource);
        throw new ResourceAccessException("I/O error on " 
                                          + method.name() +
                                          " request for \"" + resource + "\"," 
                                          + ex.getMessage(), ex);
    } finally {
        if(response ! =null) { response.close(); }}}Copy the code