Spring RestTemplate的应用

作者: 砒霜拌辣椒 | 来源:发表于2020-10-05 13:55 被阅读0次

    Spring提供了一种简单便捷的模板类 RestTemplate 来调用 RESTful 接口。它提供了多种便捷访问HTTP服务的方法,能够大大提高客户端的编写效率。

    RestTemplate方法 HTTP方法
    getForEntity GET
    getForObject GET
    postForEntity POST
    postForObject POST
    put PUT
    patch PATCH
    delete DELETE
    exchange any
    execute any

    1、RESTful API接口

    @RestController
    @Slf4j
    public class RestfulController {
        @GetMapping(value = "/getUser1", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public User getUser1() {
            return new User(1L, "zhaoxb");
        }
    
        @GetMapping(value = "/getUser2", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public User getUser2(User user) {
            log.info("getUser2:{}", JSONUtil.toJsonPrettyStr(user));
            return user;
        }
    
        @PostMapping(value = "/postUser", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public User postUser(User user) {
            log.info("postUser:{}", JSONUtil.toJsonPrettyStr(user));
            return user;
        }
        
        @PostMapping(value = "/postBody", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        public User postBody(@RequestBody User user) {
            log.info("postBody:{}", JSONUtil.toJsonPrettyStr(user));
            return user;
        }
    }
    
    1. GET请求,不带参。
    2. GET请求,带参。
    3. POST请求,带参。
    4. POST请求,带有请求体。

    实体类,需要提供有参和无参构造。

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Long id;
        private String name;
    }
    

    RestTemplate配置类

    @Configuration
    public class RestConfig {
        @Bean
        public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
            RestTemplate restTemplate = restTemplateBuilder
                    .setConnectTimeout(Duration.ofMillis(5000L))
                    .setReadTimeout(Duration.ofMillis(30000L))
                    .build();
            return restTemplate;
        }
    }
    

    2、发送GET请求

    2.1、getForEntity方法,不带参

    @ActiveProfiles("test")
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Slf4j
    public class RestfulGetTests {
        @Autowired
        private RestTemplate restTemplate;
    
        @Test
        public void getForEntity1() {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser1", String.class);
            log.info("响应码:{}", responseEntity.getStatusCodeValue());
            log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
        }
    }
    
    响应码:200
    响应体:{
        "name": "zhaoxb",
        "id": 1
    }
    

    2.2、getForEntity方法,数字占位符方式传参

    @Test
    public void getForEntity2() {
        ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser2?id={1}&name={2}", String.class, 2, "zhaoxb");
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
    }
    

    用一个数字做占位符。最后是一个可变长度的参数,用来替换前面的占位符。

    响应体:{
        "name": "zhaoxb",
        "id": 2
    }
    

    2.3、getForEntity方法,map占位符方式传参

    @Test
    public void getForEntity3() {
        Map<String, Object> map = new HashMap<>();
        map.put("id", 3);
        map.put("name", "zhaoxb");
        ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser2?id={id}&name={name}", String.class, map);
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
    }
    

    使用 name={name} 这种形式。最后一个参数是map,map的key为前边占位符的名字,value为实际参数值。

    响应体:{
        "name": "zhaoxb",
        "id": 3
    }
    

    2.4、getForEntity方法,返回对象

    @Test
    public void getForEntity4() {
        ResponseEntity<User> responseEntity = restTemplate.getForEntity("http://127.0.0.1:31000/getUser1", User.class);
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
    }
    
    响应体:{
        "name": "zhaoxb",
        "id": 1
    }
    

    2.5、getForObject方法,直接返回对象

    @Test
    public void getForObject() {
        User User = restTemplate.getForObject("http://127.0.0.1:31000/getUser1", User.class);
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(User));
    }
    

    getForObject是对getForEntity函数的进一步封装,只关注返回消息的实体内容。

    响应体:{
        "name": "zhaoxb",
        "id": 1
    }
    

    3、发送POST请求

    用post方法发送带参的请求时,Map不能被定义为 HashMapLinkedHashMap,而应被定义为 LinkedMultiValueMap,这样参数才能成功传递到后台。

    3.1、postForEntity方法

    @Test
    public void postForEntity() {
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("id", 5);
        multiValueMap.add("name", "zhaoxb");
        ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://127.0.0.1:31000/postUser", multiValueMap, User.class);
        log.info("响应码:{}", responseEntity.getStatusCodeValue());
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
    }
    
    响应体:{
        "name": "zhaoxb",
        "id": 5
    }
    

    3.2、postForObject方法

    @Test
    public void postForObject() {
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("id", 6);
        multiValueMap.add("name", "zhaoxb");
        User user = restTemplate.postForObject("http://127.0.0.1:31000/postUser", multiValueMap, User.class);
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(user));
    }
    

    getForObject 相对应,只关注返回的消息体。

    响应体:{
        "name": "zhaoxb",
        "id": 6
    }
    

    3.3、postForObject方法,带有请求体body

    @Test
    public void postForObject2() {
        User reqUser = new User(10L, "zhaoxb");
        User user = restTemplate.postForObject("http://127.0.0.1:31000/postBody", reqUser, User.class);
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(user));
    }
    

    RestTemplate底层实现序列化和反序列化。

    响应体:{
        "name": "zhaoxb",
        "id": 10
    }
    

    3.4、exchange方法

    @Test
    public void exchange() {
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("id", 7);
        multiValueMap.add("name", "zhaoxb");
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(multiValueMap);
    //      HttpEntity<MultiValueMap<String, Object>> httpEntityWithHeaders = new HttpEntity<>(multiValueMap, new HttpHeaders());
        ResponseEntity<User> responseEntity = restTemplate.exchange("http://127.0.0.1:31000/postUser", HttpMethod.POST, httpEntity, User.class);
        log.info("响应体:{}", JSONUtil.toJsonPrettyStr(responseEntity.getBody()));
    }
    

    HttpEntity还支持带有HTTP请求头的构造方法。

    响应体:{
        "name": "zhaoxb",
        "id": 7
    }
    

    RestTemplate发送PUTPATCHDELETE方法与GETPOST方法非常类似,这里不做展开。

    4、自定义template

    4.1、自定义HTTP源

    ClientHttpRequestFactory是Spring定义的一个接口,用于生产ClientHttpRequest对象,RestTemplate只是模板类,抽象了很多调用方法,而底层真正使用何种框架发送HTTP请求是通过ClientHttpRequestFactory指定的。

    RestTemplate默认使用的是SimpleClientHttpRequestFactory,其内部使用的是JDK的java.net.HttpURLConnection创建底层连接,默认是没有连接池的。可以通过 setRequestFactory 函数设置不同的HTTP源,比如 Apache HttpComponentsNettyOkHttp

    设置Apache HttpComponents为HTTP客户端源

    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
    </dependencies>
    
    @Configuration
    public class RestConfig {
        @Bean
        public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
            RestTemplate restTemplate = restTemplateBuilder
                    .setConnectTimeout(Duration.ofMillis(5000L))
                    .setReadTimeout(Duration.ofMillis(30000L))
                    .requestFactory(() -> clientHttpRequestFactory())
                    .build();
            return restTemplate;
        }
    
        @Bean
        public ClientHttpRequestFactory clientHttpRequestFactory() {
            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
    
            //开始设置连接池
            PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
            poolingHttpClientConnectionManager.setMaxTotal(100);  //最大连接数
            poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);  //同路由并发数
            httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
    
            HttpClient httpClient = httpClientBuilder.build();
            // httpClient连接配置
            HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
            // 设置超时,如果 RestTemplateBuilder 已经设置,这里就不需要设置
    //        clientHttpRequestFactory.setConnectTimeout(5 * 1000); // 连接超时
    //        clientHttpRequestFactory.setReadTimeout(30 * 1000); // 数据读取超时时间
            clientHttpRequestFactory.setConnectionRequestTimeout(30 * 1000); //连接不够用的等待时间
            return clientHttpRequestFactory;
        }
    }
    

    4.2、自定义messageConverter

    RestTemplate默认使用 jackson 来实现序列化和反序列化,默认情况下会注册MIME类型的转换器,但可以通过 setMessageConverters 函数指定其他类型的转化器。

    这里其实也可以用FastJson库的FastJsonHttpMessageConverter4类来做转换器,只是近些年FastJson屡爆漏洞,还是建议用默认的jackson来实现。

    参考链接

    代码地址

    相关文章

      网友评论

        本文标题:Spring RestTemplate的应用

        本文链接:https://www.haomeiwen.com/subject/uzrauktx.html