Spring之RestTemplate常用API实践

Posted 一宿君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring之RestTemplate常用API实践相关的知识,希望对你有一定的参考价值。

目录


1、RestTemplate简介

在开发的过程中,很多时候我们需要从当前服务端(当前项目)向外进行网络请求(其他项目)获取数据或传送数据,传统情况下,我们一般会使用JDK自带的HttpURLConnectionApache提供的HttpClientOkHttp等客户端请求工具,但是这几种方式使用起来比较繁琐,对于初学者来说不太友好,而Spring为我们提供了一个快捷方便的Restful模板类请求工具类RestTemplate

RestTemplate是一个执行HTTP请求的同步阻塞式请求工具类,它是在JDK HttpURLConnectionApache HttpComponentsOkHttp等客户端的基础上,封装了更高级别的API,使在单行中调用REST端点变得容易。RestTemplate默认是依赖JDK HttpURLConnection,可以根据实际需要通过构造函数中的setRequestFactory方法设置底层依赖请求客户端。

public RestTemplate(ClientHttpRequestFactory requestFactory) 
      this();
      this.setRequestFactory(requestFactory);//指定HTTP客户端

2、RestTemplate初始化配置

如果当前是SpringBoot项目(非spring环境),我们需要导入Spring-web依赖,即可引入RestTemplate类:

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>5.2.0.RELEASE</version>
</dependency>

如果当前是SpringBoot项目(spring环境),我们只需要导入spring-boot-starter-web依赖,即可引入RestTemplate类:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <version>2.2.0.RELEASE</version>
</dependency>

内置支持以下功能:

  • JDK HttpURLConnection
  • Apache HttpComponents
  • OkHttp

RestTemplate包含以下几个部分:

  • HttpMessageConverter对象转换器
  • ClientHttpRequestFactory 默认是JDKHttpURLConnection
  • ResponseErrorHandler 异常处理器
  • ClientHttpRequestInterceptor 请求拦截器

我们现在大多使用SpringBoot框架,为了后续方便使用,我们将RestTemplate配置初始化为一个Bean

@Configuration
public class RestTemplateConfig 

    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate()
        RestTemplate restTemplate = new RestTemplate(getSimpleClientHttpRequestFactory());
        return restTemplate;
    
    
    /**
     * 默认底层依赖JDK HttpURLConnection作为HTTP客户端
     * @return
     */
    public ClientHttpRequestFactory getSimpleClientHttpRequestFactory() 
        int timeout = 5000;
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(timeout);
        factory.setConnectTimeout(timeout);
        return factory;
    

在项目启动的时候会默认加载当前配置类中的@Bean注解方法,将restTemplate()方法注入Spring容器中,注意: @Bean注解的方法名最好是restTemplate首字母小写,后续我们在需要用到的地方直接@Autowire自动装载即可使用。

@Autowired
private RestTemplate restTemplate;

通过管方指导文档描述:

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:
释义:默认构造函数使用java.net.HttpURLConnection执行请求。您可以切换到具有`ClientHttpRequestFactory`实现的不同HTTP库。
    
//直接new RestTemplate();底层依赖的也是JDK HttpURLConnection

如果我们想切换到Apache HttpComponents客户端,可以使用以下方式:

首先导入Apache HttpClient依赖:

<!-- Apache Httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.6</version>
</dependency>

初始化:

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

但是通常情况下,我们切换客户端会预先做一些连接请求超时参数配置,如下:

@Configuration
public class RestTemplateConfig 

    /**
     * 使用Apache HttpClient作为底层客户端
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate()
		//RestTemplate restTemplate = new RestTemplate();
        RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
        return restTemplate;
    


    /**
     * 使用Apache HttpClient作为底层客户端
     * @return
     */
    private ClientHttpRequestFactory getClientHttpRequestFactory()
        int timeout = 5000;
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(timeout)
                .setConnectTimeout(timeout)
                .setSocketTimeout(timeout)
                .build();
        CloseableHttpClient client = HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .build();
        return new HttpComponentsClientHttpRequestFactory(client);
    

切换为OkHttp客户端:

先导入依赖:

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.7.2</version>
</dependency>

初始化:

RestTemplate template = new RestTemplate(new OkHttp3ClientHttpRequestFactory());

预先做一些连接请求超时参数配置,如下:

@Configuration
public class RestTemplateConfig 

    /**
     * 使用OkHTTP作为底层客户端
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate()
		//RestTemplate restTemplate = new RestTemplate();
		//RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
        RestTemplate restTemplate = new RestTemplate(getOkHttp3ClientHttpRequestFactory());
        return restTemplate;
    

    /**
     * 使用OkHTTP作为底层客户端
     * @return
     */
    private OkHttp3ClientHttpRequestFactory getOkHttp3ClientHttpRequestFactory()
        int timeout = 5000;
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttp3ClientHttpRequestFactory okHttp3ClientHttpRequestFactory = new OkHttp3ClientHttpRequestFactory(okHttpClient)
            setConnectTimeout(timeout);
            setReadTimeout(timeout);
            setWriteTimeout(timeout);
        ;
        return okHttp3ClientHttpRequestFactory;
    

具网上不完全统计,上述Http客户端的效率:

OkHttp > Apache HttpClient > JDK HttpURLConnection

所以下述我们以OkHttp作为底层客户端依赖。

扩展:

@ConditionalOnMissingBean(RestTemplate.class)

当注入多个相同类型的Bean时,会报异常,此注解就是保证当前类型Bean只能有一个实例化对象。

@ConditionalOnBean // 当给定的在bean存在时,则实例化当前Bean

@ConditionalOnMissingBean // 当给定的在bean不存在时,则实例化当前Bean

@ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean

@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean

3、RestTemplate常用API实践

Spring之RestTemplate官方指导文档。

它公开了以下几组重载方法:

方法描述
getForObject通过GET检索一个表示。
getForEntity通过使用GET检索一个ResponseEntity(即状态、标题和主体)。
headForHeaders通过使用HEAD检索资源的所有标头。
postForLocation使用POST创建一个新资源,并从响应中返回Location报头。
postForObject使用POST创建新资源,并从响应返回表示。
postForEntity使用POST创建新资源,并从响应返回表示。
put通过使用PUT创建或更新资源。
patchForObject使用PATCH更新资源,并从响应返回表示。注意,JDK HttpURLConnection 不支持PATCH,但Apache HttpComponents和其他组件支持。
delete使用DELETE删除指定URI处的资源。
optionsForAllow使用ALLOW为资源检索允许的HTTP方法。
exchange在需要时提供额外灵活性的前面方法的更一般化(且不那么自以为是)版本。它接受一个’RequestEntity (包括HTTP方法、URL、报头和主体作为输入),并返回一个ResponseEntity。这些方法允许使用ParameterizedTypeReference代替Class来指定具有泛型的响应类型。
execute执行请求的最通用方法,通过回调接口完全控制请求准备和响应提取。

将上述方法大致归为三类:

常用的Rest API(GET、POST、PUT、DELETE):

  • getForObject
  • getForEntity
  • headForHeaders
  • postForLocation
  • postForObject
  • postForEntity
  • put
  • patchForObject
  • delete
  • optionsForAllow

接收一个 RequestEntity 参数,自定义设置HTTP methodURLheadersbody,返回 ResponseEntity:

  • exchange

通过 callback 接口,可以对请求和返回做更加全面的自定义控制:

  • execute

创建一个响应实体类ResponseBean

@Data
@ToString
public class ResponseBean<T> 
    /**
     * 状态码 200 成功 其他失败
     */
    private String code;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 异常信息
     */
    private String message;

创建一个请求实体类RequestBean

@Data
public class RequestBean 
    /**
     * 用户id
     */
    private String userCode;

    /**
     * 
     */
    private String userName;

3.1、GET类型方法

通过RestTemplate发送HTTP GET协议请求,经常会用到以下两个方法:

  • getForObject():返回值是HTTP协议的响应体内容
  • getForEntity():返回的是ResponseEntityResponseEntity是对HTTP响应的封装,除了包含响应体,还包含HTTP状态码、contentType、contentLength、Header等信息

getForObject()重载方法:

@Nullable
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException 

@Nullable
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException 

@Nullable
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException 

getForEntity()重载方法:

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException 

public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException 

public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException 

3.1.1、GET无参请求方法之getForObject()

创建一个无参get请求接口:

@RestController
@RequestMapping("/rest")
public class RestTemplateController 

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/no/param/test/get")
    public ResponseBean<String> testGet1()
        ResponseBean<String> responseBean = new ResponseBean<String>();
        responseBean.setCode("200");
        responseBean.setData("无参get请求!");
        responseBean.setMessage(null);
        return responseBean;
    

单元测试无参get请求:

@SpringBootTest
@RunWith(SpringRunner.class)
public class RestTemplateTest 

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void testGet1()
        String url = "http://localhost:6625/rest/no/param/test/get";
        ResponseBean response = restTemplate.getForObject(url, ResponseBean.class);
        System.out.println(response);
    

打印结果:

"code":"200","data":"响应成功了!","message":null

发现出现了乱码,原因在于RestTemplate中对字符串默认使用的转换器是StringHttpMessageConverter,而StringHttpMessageConverter默认的字符集编码是ISO_8859_1:

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> 
    public static final Charset DEFAULT_CHARSET;

    public StringHttpMessageConverter() 
        this(DEFAULT_CHARSET);
      
    
    static 
        //默认字符集
        DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
    

ISO_8859_1编码格下,中文是乱码的。因此我们需要将编码格式设置为UTF-8的格式才能支持中文:

@Configuration
public class RestTemplateConfig 


    @Bean
    @ConditionalOnMissingBean(RestTemplate.class)
    public RestTemplate restTemplate()
        RestTemplate restTemplate = new RestTemplate(getOkHttp3ClientHttpRequestFactory());
        //设置RestTemplate的字符集为UTF-8
        setRestTemplateEncode(restTemplate);
        return restTemplate;
    

    /**
     * 使用OkHTTP作为底层客户端
     * @return
     */
    private OkHttp3ClientHttpRequestFactory getOkHttp3ClientHttpRequestFactory()
        int timeout = 5000;
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttp3ClientHttpRequestFactory okHttp3ClientHttpRequestFactory = new OkHttp3ClientHttpRequestFactory(okHttpClient)
            setConnectTimeout(timeout);
            setReadTimeout(timeout);
            setWriteTimeout(timeout);
        ;
        return okHttp3ClientHttpRequestFactory;
    

    /**
     * 指定设置RestTemplate的字符转换器编码为UTF_8
     * @param restTemplate
     */
    public static void setRestTemplateEncode(RestTemplate restTemplate) 
        if (ObjectUtils.isEmpty(restTemplate) || ObjectUtils.isEmpty(restTemplate.getMessageConverters()))
            return;
        
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        for (int i = 0; i < messageConverters.size(); i++) 
            HttpMessageConverter<?> httpMessageConverter = messageConverters.get(i);
            if (StringHttpMessageConverter.class.equals(httpMessageConverter.getClass()))
                messageConverters.set(i,new StringHttpMessageConverter(StandardCharsets.UTF_8));
            
        
    

再次重启,打印结果:

ResponseBean(code=200, data=无参get请求!, message=null)

问题解决!

3.1.2、GET占位符传参请求方法之getForObject()

创建一个占位符传参get请求接口:

@RestController
@RequestMapping("/rest")
public class RestTemplateController 

    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/path/param/test/get/code/data")
    public ResponseBean<String> testGet2(@PathVariable("code") String code,@PathVariable("data") String data)
        ResponseBean<String> responseBean = new ResponseBean<String>();
        responseBean.setCode(code);
        responseBean.setData(data);
        responseBean.setMessage(null);
        return responseBean;
    

单元测试占位符传参get请求:

@SpringBootTest
@RunWith(SpringRunner.class)
public class RestTemplateTest 

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void testGet2()
        String url = "http://localhost:6625/rest/path/param/test/get/code/data";
        ResponseBean response = restTemplate.getForObject(url, ResponseBean.class,"200","占位符传参get请求&

以上是关于Spring之RestTemplate常用API实践的主要内容,如果未能解决你的问题,请参考以下文章

小白一看就会的Spring的RestTemplate的使用

JavaSpring之RestTemplate的使用

Spring RestTemplate 之get请求

一起学 Spring 之 RestTemplate

使用 spring restTemplate 对 REST API 进行基本身份验证

Spring RestTemplate 配置策略从单个 API 调用多个休息服务