How to use RestTemplate in Spring boot, part II

How to use RestTemplate in Spring boot, part II

  • Consumer Service
    • pom
    • Controller
    • Business
      • Consumer Business
      • ConsumerBusinessImpl
    • Result
      • MaoResult
      • MaoResultCode
    • request
    • Config
      • application.yml
      • JurongConfig
      • knife4jConfig
  • Summary

Considering the length of the article, here is an article divided into two parts, we will continue to introduce on the basis of How to use RestTemplate in Spring boot, part I.

Consumer Service

pom

<?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>

    <parent>
        <groupId>com.qwfys.sample</groupId>
        <artifactId>maoshan</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <groupId>com.qwfys.sample</groupId>
    <artifactId>maoshan-jurong</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.qwfys.sample</groupId>
            <artifactId>maoshan-common</artifactId>
        </dependency>
    </dependencies>

</project>

Controller

package com.qwfys.sample.maoshan.jurong.controller;

import com.qwfys.sample.maoshan.common.vo.AccountDetailVO;
import com.qwfys.sample.maoshan.jurong.business.spec.ConsumerBusiness;
import com.qwfys.sample.maoshan.jurong.comon.result.MaoResultCode;
import com.qwfys.sample.maoshan.jurong.comon.result.MaoResult;
import com.qwfys.sample.maoshan.jurong.request.AccountDetailRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@Tag(name = "Consumer Management")
public class ConsumerController {<!-- -->

    @Autowired
    private ConsumerBusiness consumerBusiness;

    @PostMapping("/consumer/account/detail")
    @Operation(summary = "Get consumer account details")
    public MaoResult<AccountDetailVO> viewAccountDetail(@RequestHeader("Authorization") String token, @RequestBody AccountDetailRequest param) {<!-- -->
        MaoResult<AccountDetailVO> result = null;
        try {<!-- -->
            AccountDetailVO detailVO = consumerBusiness.viewAccountDetail(token, param);
            result = MaoResult. success(detailVO);

        } catch (Exception e) {<!-- -->
            log. error(e. getMessage(), e);
            result = MaoResult. fail(MaoResultCode. EXCEPTION);
        }

        log.info("response: {}", result);
        return result;
    }
}

Business

Consumer Business

package com.qwfys.sample.maoshan.jurong.business.spec;

import com.qwfys.sample.maoshan.common.vo.AccountDetailVO;
import com.qwfys.sample.maoshan.jurong.request.AccountDetailRequest;

/**
 * @author liuwenke
 * @since 0.0.1
 */
public interface ConsumerBusiness {<!-- -->
    AccountDetailVO viewAccountDetail(String token, AccountDetailRequest param);
}

ConsumerBusinessImpl

package com.qwfys.sample.maoshan.jurong.business.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qwfys.sample.maoshan.common.result.HuaResult;
import com.qwfys.sample.maoshan.common.vo.AccountDetailVO;
import com.qwfys.sample.maoshan.jurong.business.spec.ConsumerBusiness;
import com.qwfys.sample.maoshan.jurong.request.AccountDetailRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

/**
 * @author liuwenke
 * @since 0.0.1
 */

@Slf4j
@Service
public class ConsumerBusinessImpl implements ConsumerBusiness {<!-- -->

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public AccountDetailVO viewAccountDetail(String token, AccountDetailRequest param) {<!-- -->
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", token);
        headers.setContentType(MediaType.APPLICATION_PROBLEM_JSON);
        ObjectMapper mapper = new ObjectMapper();
        String body = null;
        try {<!-- -->
            body = mapper. writeValueAsString(param);
        } catch (JsonProcessingException e) {<!-- -->
            log. error(e. getMessage(), e);
            throw new RuntimeException(e);
        }
        HttpEntity<?> httpEntity = new HttpEntity<>(body, headers);

        String apiUrl = "http://127.0.0.1:19000/provider/account/detail";
        HttpMethod httpMethod = HttpMethod. POST;
        ResponseEntity<HuaResult<AccountDetailVO>> responseEntity = restTemplate. exchange(
                apiUrl,
                httpMethod,
                httpEntity,
                new ParameterizedTypeReference<HuaResult<AccountDetailVO>>() {<!-- -->
                }
        );

        Assert.notNull(responseEntity, "responseEntity is empty");
        HuaResult<AccountDetailVO> huaResult = responseEntity.getBody();
        Assert.notNull(huaResult, "result cannot be empty");
        Assert.isTrue(huaResult.getIsSuccess(), "code:" + huaResult.getResultCode() + "message:" + huaResult.getResultMessage());
        AccountDetailVO accountDetail = huaResult.getData();
        return accountDetail;
    }
}

Result

MaoResult

package com.qwfys.sample.maoshan.jurong.comon.result;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok. AllArgsConstructor;
import lombok. Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;

@Slf4j
@Builder
@Data
@AllArgsConstructor
@Schema(description = "Uniformly return entities")
public class MaoResult<T> {<!-- -->

    /**
     * status code
     */
    private String code;

    /**
     * information
     */
    private String msg;

    /**
     * data
     */
    private T data;

    public MaoResult() {<!-- -->
    }

    public boolean isSuccess() {<!-- -->
        return Objects.equals(MaoResultCode.OK.value(), this.code);
    }
    public boolean isFail() {<!-- -->
        return !Objects.equals(MaoResultCode.OK.value(), this.code);
    }

    public static <T> MaoResult<T> success(T data) {<!-- -->
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setData(data);
        maoResult.setCode(MaoResultCode.OK.value());
        return maoResult;
    }

    public static <T> MaoResult<T> success() {<!-- -->
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setCode(MaoResultCode.OK.value());
        maoResult.setMsg(MaoResultCode.OK.getMsg());
        return maoResult;
    }

    public static <T> MaoResult<T> success(Integer code, T data) {<!-- -->
        return success(String. valueOf(code), data);
    }

    public static <T> MaoResult<T> success(String code, T data) {<!-- -->
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult. setCode(code);
        maoResult.setData(data);
        return maoResult;
    }

    public static <T> MaoResult<T> showFailMsg(String msg) {<!-- -->
        log. error(msg);
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setMsg(msg);
        maoResult.setCode(MaoResultCode.SHOW_FAIL.value());
        return maoResult;
    }

    public static <T> MaoResult<T> fail(MaoResultCode maoResultCode) {<!-- -->
        log.error(maoResultCode.toString());
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setMsg(maoResultCode.getMsg());
        maoResult.setCode(maoResultCode.value());
        return maoResult;
    }

    public static <T> MaoResult<T> fail(MaoResultCode maoResultCode, T data) {<!-- -->
        log.error(maoResultCode.toString());
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setMsg(maoResultCode.getMsg());
        maoResult.setCode(maoResultCode.value());
        maoResult.setData(data);
        return maoResult;
    }

    public static <T> MaoResult<T> fail(String code, String msg, T data) {<!-- -->
        log. error(msg);
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setMsg(msg);
        maoResult. setCode(code);
        maoResult.setData(data);
        return maoResult;
    }

    public static <T> MaoResult<T> fail(String code, String msg) {<!-- -->
        return fail(code, msg, null);
    }

    public static <T> MaoResult<T> fail(Integer code, T data) {<!-- -->
        MaoResult<T> maoResult = new MaoResult<>();
        maoResult.setCode(String.valueOf(code));
        maoResult.setData(data);
        return maoResult;
    }
}

MaoResultCode

package com.qwfys.sample.maoshan.jurong.comon.result;

import lombok. ToString;

/**
 * @author liuwenke
 * @since 0.0.1
 */
@ToString
public enum MaoResultCode {<!-- -->

    OK("00000", "ok"),

    SHOW_FAIL("A00001", ""),

    REMOTE_CALL_FAIL("REMOTE_CALL_FAIL", "Remote interface call failed"),

    PARSING_JSON_FAIL("PARSING_JSON_FAIL", "JSON parsing failed"),

    /**
     * Used to directly display the success of the prompt system, the content is determined by the input content
     */
    SHOW_SUCCESS("A00002", ""),

    /**
     * unauthorized
     */
    UNAUTHORIZED("A00004", "Unauthorized"),

    /**
     * The server is out of order
     */
    EXCEPTION("A00005", "The server is out of order"),

    /**
     * There is no verification of method parameters, and the content is determined by the input content
     */
    METHOD_ARGUMENT_NOT_VALID("A00014", "Method parameters are not verified");

    private final String code;

    private final String msg;

    public String value() {<!-- -->
        return code;
    }

    public String getMsg() {<!-- -->
        return msg;
    }

    MaoResultCode(String code, String msg) {<!-- -->
        this.code = code;
        this.msg = msg;
    }
}

Request

package com.qwfys.sample.maoshan.jurong.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;

@Schema(description = "Account Details Request Parameters")
@Data
@Accessors(chain = true)
public class AccountDetailRequest {<!-- -->

    @Schema(description = "UserID")
    private Long userId;
}

Config

application.yml

mybatis-plus:
  global-config:
    banner: false

server:
  port: 19001

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:23306/jurong?allowMultiQueries=true &useSSL=false &useUnicode=true &characterEncoding=UTF-8 &autoReconnect=true &zeroDateTimeBehavior= convertToNull &useJDBCCompliantTimezoneShift=true &useLegacyDatetimeCode=false &serverTimezone=GMT+8 &nullCatalogMeansCurrent=true &allowPublicKeyRetrieval=true
    username: root
    password: Gah6kuP7ohfio4
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 0
      maximum-pool-size: 20
      idle-timeout: 10000
      auto-commit: true
      connection-test-query: SELECT 1

JurongConfig

package com.qwfys.sample.maoshan.jurong.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * @author liuwenke
 * @since 0.0.1
 */
@Configuration
public class JurongConfig {<!-- -->

    private static final int CONNECT_TIMEOUT = 8000;

    private static final int SOCKET_TIMEOUT = 8000;

    @Bean
    public RestTemplate restTemplate() {<!-- -->
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        //factory.setConnectTimeout(CONNECT_TIMEOUT);
        //factory.setReadTimeout(SOCKET_TIMEOUT);
        return new RestTemplate(factory);
        //return new RestTemplate();
    }
}

knife4jConfig

package com.qwfys.sample.maoshan.jurong.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author liuwenke
 * @since 0.0.1
 */
@Configuration
public class knife4jConfig {<!-- -->

    @Bean
    public GroupedOpenApi baseRestApi() {<!-- -->
        return GroupedOpenApi. builder()
                .group("Interface Documentation")
                .packagesToScan("com.qwfys.sample.maoshan")
                .build();
    }

    @Bean
    public OpenAPI springShopOpenApi() {<!-- -->
        return new OpenAPI()
                .info(
                        new Info()
                                .title("Service Consumer Interface Documentation")
                                .description("Service Consumer interface document")
                                .version("0.0.1-SNAPSHOT")
                                .license(
                                        new License()
                                                .name("Please abide by the MIT License authorization agreement")
                                                .url("https://github.com/ab-sample/maoshan")
                                )
                );
    }
}

Summary

1. After obtaining the json data with the help of RestTemplate, many times, we expect to deserialize the json data into Java Bean objects.

If the Java Bean does not contain generics, you can use the following methods to receive and deserialize data:

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {<!-- -->
//...
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
throws RestClientException {<!-- -->

RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
}
//...
}

Pass the class corresponding to the Java Bean after json deserialization to the parameter responseType of the method.

If the Java Bean contains generics, the following methods can be used to complete data reception and deserialization:

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {<!-- -->
//...
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {<!-- -->

Type type = responseType. getType();
RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
}
//...
}

At this time, we need to instantiate a ParameterizedTypeReference object. When instantiating, we need to instantiate the deserialized Java Bean as the generic parameter of ParameterizedTypeReference, such as:

public AccountDetailVO viewAccountDetail(String token, AccountDetailRequest param) {<!-- -->
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", token);
        headers.setContentType(MediaType.APPLICATION_PROBLEM_JSON);
        ObjectMapper mapper = new ObjectMapper();
        String body = null;
        try {<!-- -->
            body = mapper. writeValueAsString(param);
        } catch (JsonProcessingException e) {<!-- -->
            log. error(e. getMessage(), e);
            throw new RuntimeException(e);
        }
        HttpEntity<?> httpEntity = new HttpEntity<>(body, headers);

        String apiUrl = "http://127.0.0.1:19000/provider/account/detail";
        HttpMethod httpMethod = HttpMethod. POST;
        ResponseEntity<HuaResult<AccountDetailVO>> responseEntity = restTemplate. exchange(
                apiUrl,
                httpMethod,
                httpEntity,
                new ParameterizedTypeReference<HuaResult<AccountDetailVO>>() {<!-- -->
                }
        );

        Assert.notNull(responseEntity, "responseEntity is empty");
        HuaResult<AccountDetailVO> huaResult = responseEntity.getBody();
        Assert.notNull(huaResult, "result cannot be empty");
        Assert.isTrue(huaResult.getIsSuccess(), "code:" + huaResult.getResultCode() + "message:" + huaResult.getResultMessage());
        AccountDetailVO accountDetail = huaResult.getData();
        return accountDetail;
    }

Here, HuaResult is passed in as a generic parameter of ParameterizedTypeReference to complete the instantiation.

2. If you want to customize the RestTemplate instance, you can implement it based on the corresponding factory method when creating it, such as:

@Configuration
public class JurongConfig {<!-- -->

    private static final int CONNECT_TIMEOUT = 8000;

    private static final int SOCKET_TIMEOUT = 8000;

    @Bean
    public RestTemplate restTemplate() {<!-- -->
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(CONNECT_TIMEOUT);
        factory.setReadTimeout(SOCKET_TIMEOUT);
        return new RestTemplate(factory);
        //return new RestTemplate();
    }
}

If you just use it briefly, you can complete the instantiation as follows:

@Configuration
public class JurongConfig {<!-- -->
    @Bean
    public RestTemplate restTemplate() {<!-- -->
        return new RestTemplate();
    }
}

I put the complete sample code in the maoshan code repository on github. If you want to see the complete code, you can download it from github.