您现在的位置是:首页 >学无止境 >How to use RestTemplate in Spring boot, part II网站首页学无止境

How to use RestTemplate in Spring boot, part II

qwfys200 2024-06-22 12:01:02
简介How to use RestTemplate in Spring boot, part II

考虑到篇幅问题,这里将一篇文章切割成了两部分,我们将在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 = "消费方管理")
public class ConsumerController {

    @Autowired
    private ConsumerBusiness consumerBusiness;

    @PostMapping("/consumer/account/detail")
    @Operation(summary = "获取消费方账号详情")
    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

ConsumerBusiness

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为空");
        HuaResult<AccountDetailVO> huaResult = responseEntity.getBody();
        Assert.notNull(huaResult, "result不能为空");
        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 = "统一返回实体")
public class MaoResult<T> {

    /**
     * 状态码
     */
    private String code;

    /**
     * 信息
     */
    private String msg;

    /**
     * 数据
     */
    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", "远程接口调用失败"),

    PARSING_JSON_FAIL("PARSING_JSON_FAIL", "JSON解析失败"),

    /**
     * 用于直接显示提示系统的成功,内容由输入内容决定
     */
    SHOW_SUCCESS("A00002", ""),

    /**
     * 未授权
     */
    UNAUTHORIZED("A00004", "Unauthorized"),

    /**
     * 服务器出了点小差
     */
    EXCEPTION("A00005", "服务器出了点小差"),

    /**
     * 方法参数没有校验,内容由输入内容决定
     */
    METHOD_ARGUMENT_NOT_VALID("A00014", "方法参数没有校验");

    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 = "账号详情请求参数")
@Data
@Accessors(chain = true)
public class AccountDetailRequest {

    @Schema(description = "用户ID")
    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%2B8&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("接口文档")
                .packagesToScan("com.qwfys.sample.maoshan")
                .build();
    }

    @Bean
    public OpenAPI springShopOpenApi() {
        return new OpenAPI()
                .info(
                        new Info()
                                .title("Service Consumer接口文档")
                                .description("Service Consumer接口文档")
                                .version("0.0.1-SNAPSHOT")
                                .license(
                                        new License()
                                                .name("使用请遵守MIT License授权协议")
                                                .url("https://github.com/ab-sample/maoshan")
                                )
                );
    }
}

Summary

1、借助RestTemplate取到json数据以后,很多时候,我们都期望将json数据反序列化为Java Bean对象。

如果Java Bean不包含泛型,可以借助如下方法完成数据的接收与反序列化:

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));
	}
}

将json反序列化后的Java Bean对应的class传给方法的参数responseType。

如果Java Bean包含泛型,可以借助如下方法完成数据的接收与反序列化:

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));
	}
}

这个时候,我们要实例化一个ParameterizedTypeReference的对象出来,实例化的时候,需要将json反序列化后的Java Bean做为ParameterizedTypeReference的泛型参数来实例化,如:

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为空");
        HuaResult<AccountDetailVO> huaResult = responseEntity.getBody();
        Assert.notNull(huaResult, "result不能为空");
        Assert.isTrue(huaResult.getIsSuccess(), "code:" + huaResult.getResultCode() + " message:" + huaResult.getResultMessage());
        AccountDetailVO accountDetail = huaResult.getData();
        return accountDetail;
    }

这里将HuaResult<AccountDetailVO>做为ParameterizedTypeReference的泛型参数传入,完成实例化。

2、如果要对RestTemplate实例做定制化,在创建的时候,可以基于相应的工厂方法实现,如:

@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();
    }
}

如果只是简单用一下,可以用如下方式完成实例化:

@Configuration
public class JurongConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

完整的样例代码我放到github上的maoshan代码仓库了,如果想看完整的代码可以到github下载。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。