RestTemplate使用说明

RestTemplate是Spring提供的用于访问http接口的客户端工具,它提供了多种便捷访问http服务的方法,并且,它的设计完全遵循Rest风格。在任意SpringBoot项目中,都有默认集成,不用引入任何多余的jar包。相比较传统的HttpClient、Okhttp以及java原生的URLConnection,有着极高的封装度,能够大大的提高对第三方http接口访问的编写效率。

配置RestTemplate

RestTemplate工具提供两种装配方式。一种是RestTemplate类,一种是RestTemplateBuilder,其中,Spring已默认将RestTemplateBuilder装配为了Bean,直接使用即可。

这两种创建方式,各有优劣势。针对于RestTemplate类来讲,去创建一些错误处理拦截、添加默认请求参数比较容易,针对RestTemplateBuilder来讲,去设置连接超时和读超时比较容易。

比如单用RestTemplate去配置,如果要设置RestTemplate的连接超时,需要通过ClientHttpRequestFactory接口去创建,就比较麻烦。伪代码如下:

@Bean
public RestTemplate initrRestTemplate() {
		RestTemplate restTemplate = new RestTemplate();
		restTemplate.setRequestFactory(ClientHttpRequestFactory requestFactory);
		// TODO 通过实现ClientHttpRequestFactory接口,或者使用Spring中其它已经实现了该接口的类,等一些列操作去设置RestTemplate的连接超时
		return restTemplate;
}

 有句成语叫取长补短,所以,使用两个类糅合在一起去创建,就比较nice。如下:

@Bean
public RestTemplate initRestTemplate(RestTemplateBuilder restTemplateBuilder) {
		// 设置连接超时和读超时都是6000毫秒
		final int TIMEOUT = 6000;
		RestTemplate restTemplate = restTemplateBuilder.setConnectTimeout(Duration.ofMillis(TIMEOUT))
														.setReadTimeout(Duration.ofMillis(TIMEOUT))
														.build();
		// TODO 接着再使用RestTemplate去配置。RestTemplate提供以下配置
		// 响应异常处理。应用场景:比如访问某一个系统,它的响应状态200是正常,就可以做一个统一的非200响应码的拦截
		// restTemplate.setErrorHandler(ResponseErrorHandler errorHandler);
		// 设置全局的默认请求参数.使用场景:比如访问某一个系统,所有的url都需要携带token请求参数,那么就可以使用此设置
		// restTemplate.setDefaultUriVariables(Map<String, ?> uriVars);
		// 设置请求拦截。应用场景:比如访问某个系统,需要鉴权或者添加一些全局的请求头等信息,这里可以创建多个请求拦截,执行顺序和list中对象的顺序一致
		// restTemplate.setInterceptors(List<ClientHttpRequestInterceptor> interceptors);
		// 设置信息转换对象.使用场景:比如设置一些编码格式、数据类型、json/xml等的类型转换器等。
		// restTemplate.setMessageConverters(List<HttpMessageConverter<?>> messageConverters);
		// 创建http请求工厂.使用场景:比如前面说到的设置连接超时、访问超时等。默认使用的是org.springframework.http.client.SimpleClientHttpRequestFactory
		// restTemplate.setRequestFactory(ClientHttpRequestFactory requestFactory);
		// 配置一个扩展uri模板的策略。使用场景:添加一些统一的请求前缀等.
		// restTemplate.setUriTemplateHandler(UriTemplateHandler handler);
		return restTemplate;
}

对Rest风格请求的支持

FBI WARNING 1:RestTemplate封装了GET/POST/PUT/DELETE这些请求方式的Rest风格请求方法。它们最终都会交给一个exchange()方法去执行,所以当面临RestTemplate所提供的封装方法不足以满足应用场景时,都可以考虑使用exchange()去实现。

FBI WARNING 2:RestTemplate是一款标准Rest风格(划重点)的http请求工具,用不好,它就是坑。

比如说:在Rest风格的定义中,GET请求一般用于表示获取某个或全部资源,获取单个资源url一般会设计成这样:http://ip:port/xxx/{资源标识}/yyy,获取全部资源则是:http://ip:port/xxx/yyy,大不了在url上再携带一个token,比如:http://ip:port/xxx/yyy?token=zepal。一般是不会去定义额外的参数的。

那么问题来了,RestTemplate对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<T> responseType, Map<String, ?> uriVariables);
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType);
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);

其中有两个表示api请求参数的形参:Object... uriVariables和Map<String, ?> uriVariables。上面说到,RestTemplate是一款标准Rest风格的工具。那么,使用这两个形参定义的请求参数,只能被http://ip:port/xxx/{资源标识}/yyy这种api接口所识别,对应的,在SpringMVC中,可以利用取@PathVariable获取{资源标识}参数,接口中定义的表单形式的参数是没办法识别的。

天天加班,哪有么多标准Rest风格的url给你用。所以,市面上大多数接口的参数都是表单形式,那么要使用GET请求这么办,只有自己去拼接url,如下:http://ip:port/xxx/yyy?key1=value1&key2=value2,或者专用exchange()去实现。

同样的POST请求封装的方法,提供的形参Object... uriVariables和Map<String, ?> uriVariables,也是用于http://ip:port/xxx/{资源标识}/yyy这种接口获取参数的。但是POST请求所表示的接口一般是会设计表单参数的。比如:在某个组织下新增一名员工,一般会设计成url:http://ip:port/dept/{deptId}/save,然后定义表单参数:empName,empPhone等。POST请求的重载方法伪代码如下:

public <T> T postForObject(String url, Object request, Class<T> responseType);
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
public <T> T postForObject(String url, Object request, Class<T> responseType);
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);

 可以看到,POST定义的形参比GET多一个,真正用于表单参数的形参是Object request。这个形参支持任何自定义对象,以及携带请求头等。

在Rest风格中,PUT表示修改操作,DELETE表示删除操作。所以针对PUT和DELTE的请求方法,RestTemplate也就没有设计相应的返回值,它们请求参数设计类似于GET和POST。如果实在要获取PUT和DELETE的返回值,如上面所说,就可以使用exchange()方法去实现。

关于RestTemplate的返回类型说明

查看RestTemplate提供用于http访问的方法,有这么两种返回方式。一种是:自定义的泛型T;一种是:RestTemplate提供的泛型类ResponseEntity<T>。

自定义泛型T

在目前市面上的api中,一般常见的响应类型都会定义为json或者xml。将返回类型定义为String,那么RestTemplate就会将api响应的数据,以json字符串/xml字符串的形式返回,如下:

String result = restTemplate.getForObject("http://localhost:9000/restTemplate/api1", String.class);

如果定义为自定义的对象,那么RestTemplate的内置转换器就会将api的响应数据转换为自定义的对象(如果是xml,需要定义HttpMessageConverter对xml的支持),如下:

// 这个UserDTO就是自定义的对象
UserDTO serDTO= restTemplate.getForObject("http://localhost:9000/restTemplate/api1", UserDTO.class);

ResponseEntity<T>

它会将自定义返回对象封装body属性,同时,它还包含了响应头,以及http响应状态码(200、100、404之类的)等一系列信息封装到它的属性中去,也就是一个标准的http响应该有的东西:响应头、响应行(包括HTTP版本,状态码,状态码的原因短语)和响应体。如下:

ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://localhost:9000/restTemplate/api0", String.class);
// 响应状态码
int statusCodeValue = responseEntity.getStatusCodeValue();
// 响应头
HttpHeaders headers = responseEntity.getHeaders();
// 响应行
HttpStatus httpStatus = responseEntity.getStatusCode();
// 响应体:这个响应行就是自定义的类型,类似于上面的泛型T
String body = responseEntity.getBody();

RestTemplate发送http请求

http所提供的http请求方法都大同小异,它对GET/POST/PUT/DELETE等不同请求方式的差异都来源于标准的Rest风格,上面已作详细说明,这里就不再重复累赘。

注意点:

1、RestTemplate提供的形参:Object... uriVariables和Map<String, ?> uriVariables是用于标准Rest风格的url中的参数(比如这个url:http://ip:port/xxx/{资源标识}/yyy,获取{资源标识}参数),不支持表单参数,如果要携带表单参数,只能使用拼接url的方式或者专用exchange()方法;

2、在标准的Rest风格中,GET/DELETE请求是不建议携带自定义请求头的,所以RestTemplate没有提供可以携带自定义请求头的GET/DELETE方法,也需要转exchange()方法。

3、针对于需要携带表单类型参数的POST和PUT请求来讲,RestTemplate提供的形参Object request,如果需要使用Map这种key-value数据结构的类型来讲,需要使用MultiValueMap和其实现类LinkedMultiValueMap,不要直接使用Map接口,打debug进行相应的调用方法之后,在源码有如下一个判断,强制使用MultiValueMap,如下:

20210527225630732.png

正确的示例如下:

// ****不支持****
Map<String, String> paramA = new HashMap<String, String>();
// ****不支持****
Map<String, String> paramB = new LinkedHashMap<String, String>();
// 支持
MultiValueMap<String, String> paramC = new LinkedMultiValueMap<String, String>();
String jsonResult = restTemplate.postForObject("http://localhost:8080/zepal", paramC, String.class);

4、使用HttpEntity<T>去构造请求体的时候,如果要使用key-value结构的数据类型,同样需要使用MultiValueMap和其实现类LinkedMultiValueMap,不要直接使用Map,同上。

携带请求头和请求体(以下以exchange()方法为例,在对应的请求方法中找不到相应的封装,可以照此进行,比如GET):

// 构造请求头:如果业务场景不需要,则不用创建
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("token", "zepal");
// 构造请求体:如果业务场景不需要,则不用创建(这个请求体可以是自定义的其他任意对象)
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<String, String>();
requestBody.add("username", "zepal");
requestBody.add("password", "123456");
// 创建整个http请求结构,泛型是请求体的类型
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(requestBody, requestHeaders);
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8080/zepal", HttpMethod.GET, requestEntity, String.class);

json请求

// 构建请求头
HttpHeaders requestHeaders = new HttpHeaders();
// 自定义请求头:如果业务场景不需要,则不用添加
requestHeaders.add("token", "zepal");
// 设置媒体类型为json
requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);

// 这里还可以设置http的其他请求头.
// requestHeaders.setAcceptCharset(List<Charset> acceptableCharsets);
// 设置请求体的编码格式.有一些老国企的接口,就要求使用GBK的编码格式.WTF
// headers.add(HttpHeaders.CONTENT_ENCODING, "GBK");

// 当然,上面设置的ContentType或者AcceptChart,它也会默认转换为原生的"Content-Type:xxx"
// "Accept-Charset:xxx",所以,也可以直接使用requestHeaders.add("Content-Type", "application/json");这种方法。

// 创建json字符串作为请求体
String json = "{我是json字符串}";
// 创建整个http请求结构, 这时的请求体是json字符串,所以泛型是String
HttpEntity<String> requestEntity = new HttpEntity<String>(json, requestHeaders);
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:8080/zepal", HttpMethod.GET, requestEntity, String.class);

 更多操作有待发掘。

总结

按Rest风格去理解RestTemplate就对了。

按照HTTP的请求结构(请求头、请求行和请求体)和响应结构(响应头、响应行和响应体)去使用RestTemplate就对了。

香......

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值