场景
在日常开发中,经常会涉及到与其他系统进行交互的场景。且交互方式多种多样,本文主要介绍使用Spring中的RestTemplate与其他微服务进行系统交互。
虽然都是微服务架构的系统,但微服务中使用的组件并不完全相同,尤其是注册中心。且不同的公司,多多少少都有自己对框架的改造在里面。所以就导致不容易通过变更注册中心后,通过服务名称路由的形式进行微服务的访问。但对方还主要是以RESTful形式暴露出来的接口。这样使用RestTemplate访问就非常容易了,除了RestTemplate也可以OpenFeign,WebClient等形式进行访问,技术选型根据不同的应用场景而定。
简介
如果要了解一项技术,最权威的一定是该技术的官网,那么我们就先来看一段官网的介绍。RestTemplate API官方文档
The RestTemplate provides a higher level API over HTTP client libraries. It makes it easy to invoke REST endpoints in a single line.
Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others.
The RestTemplate offers templates for common scenarios by HTTP method, in addition to the generalized exchange and execute methods that support of less frequent cases.
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.
从这里可以看出来RestTemplate 提供了一个更高阶的访问HTTP请求的API库。RestTemplate采用同步方式执行 HTTP 请求的类,底层默认使用 JDK 原生HttpURLConnectionAPI ,但其集成了Apache 的HttpComponents
,Netty
,OkHttp
的 HTTP 客户端请求类库,我们可以通过实现ClientHttpRequestFactory
来切换不同的HTTP类库。还有一处强调的就是RestTemplate提供模板化的方法让开发者能更简单地发送 HTTP 请求。所以RestTemplate是一个同步的并且简化了HTTP请求调用方式的类库。
最后一段需注意的是,RestTemplate类是在 Spring Framework 3.0 开始引入的,这里我们使用的 Spring 版本为当前最新的 GA 版本 5.1.6。而在 5.0 以上,官方标注了更推荐使用非阻塞的响应式 HTTP 请求处理类org.springframework.web.reactive.client.WebClient
来替代RestTemplate,尤其是对应异步请求处理的场景上 。
那么接下来我们就看一下这个工具如何使用以及该工具类都有那些方法。这里参考的是Spring官方文档中总结的内容Using RestTemplate
方法名 | 描述 |
---|---|
getForObject | 通过 GET 请求获得响应结果 |
getForEntity | 通过 GET 请求获取 ResponseEntity 对象,包容有状态码,响应头和响应数据 |
headForHeaders | 以 HEAD 请求资源返回所有响应头信息 |
postForLocation | 用 POST 请求创建资源,并返回响应数据中响应头的字段 Location 的数据 |
postForObject | 通过 POST 请求创建资源,获得响应结果 |
put | 通过 PUT 方式请求来创建或者更新资源 |
patchForObject | 通过 PATH 方式请求来更新资源,并获得响应结果。(JDK HttpURLConnection 不支持 PATH 方式请求,其他 HTTP 客户端库支持) |
delete | 通过 DELETE 方式删除资源 |
optionsForAllow | 通过 ALLOW 方式请求来获得资源所允许访问的所有 HTTP 方法,可用看某个请求支持哪些请求方式 |
exchange | 更通用版本的请求处理方法,接受一个 RequestEntity 对象,可以设置路径,请求头,请求信息等,最后返回一个 ResponseEntity 实体 |
execute | 最通用的执行 HTTP 请求的方法,上面所有方法都是基于 execute 的封装,全面控制请求信息,并通过回调接口获得响应数据 |
其实RestTemplate中的这些方法主要来源于实现RestOperations
接口以及继承InterceptingHttpAccessor
类所获得的。
RestTemplate Diagrams
实战编码 (小试牛刀)
1. 添加依赖
<!-- 配置 web 依赖包 -->
<dependency>
<groupId>org.springframework.boot
<artifactId>spring-boot-starter-web
</dependency>
2. 配置RestTemplate
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
/**
* 注入RestTemplate
* @return
*/
@Bean
public RestTemplategetRestTemplate(){
SimpleClientHttpRequestFactory clientHttpRequestFactory =new SimpleClientHttpRequestFactory();
//Connect timeout 10秒钟
clientHttpRequestFactory.setConnectTimeout(10_000);
//Read timeout 10秒钟
clientHttpRequestFactory.setReadTimeout(10_000);
System.out.println("RestTemplate ------------初始化");
return new RestTemplate(clientHttpRequestFactory);
}
}
3.配置好RestTemplate后我们需要添加一些被调用的接口
@RestController
@RequestMapping("/restTemplate")
public class RestTemplateController {
/**
* 测试带参 Get请求
* @param param
* @return
*/
@GetMapping("getTemp01")
public StringgetTemp01(@RequestParam String param){
System.out.println("requestParam : " + ToStringBuilder.reflectionToString(param));
return param;
}
/**
* 测试带参 POST请求
* @param param
* @return
*/
@PostMapping("postTemp01")
public MappostTemp01(@RequestBody Map param){
System.out.println("requestParam : " + ToStringBuilder.reflectionToString(param));
return param;
}
/**
* 测试带参 POST请求
* @param key
* @return
*/
@PostMapping("postTemp02")
public StringpostTemp02(@RequestParam String key){
System.out.println("requestParam : " + ToStringBuilder.reflectionToString(key));
return key;
}
}
4.接下来我们进行RestTemplate中方法的调用,目前只整理了简单的Get请求和Post请求。(代码放上格式有点起飞 科科~~~)
@SpringBootTest
class DemoLearnApplicationTests {
@Autowired
private RestTemplate restTemplate;
@Test
public void RestTemplateTest(){
/* get 请求 */
String urlForGet = "http://127.0.0.1:8081//restTemplate/getTemp01?param={param}";
Map paramForGet = new HashMap();
paramForGet.put("param","getParam");
ResponseEntity<String> forEntity = restTemplate.getForEntity(urlForGet, String.class, paramForGet);
System.out.println("getForEntity result :"+forEntity.toString());
String forObject = restTemplate.getForObject(urlForGet, String.class, paramForGet);
System.out.println("forObject result :"+forObject);
/* post 请求 01*/
String urlForPost01 = "http://127.0.0.1:8081//restTemplate/postTemp01";
Map paramForPost01 = new HashMap();
paramForPost01.put("param","getParam");
ResponseEntity<Map> forPostEntity = restTemplate.postForEntity(urlForPost01 ,paramForPost01, Map.class);
System.out.println("forPostEntity result :"+forPostEntity.toString());
Map forPostObject = restTemplate.postForObject(urlForPost01 ,paramForPost01, Map.class);
System.out.println("forPostObject result :"+forPostObject);
/* post 请求 02*/
String urlForPost02 = "http://127.0.0.1:8081//restTemplate/postTemp02";
/* MultiValueMap 为表单提交形式 ,对方如果为参数形式接参数,此处的Map实现类只能用 MultiValueMap*/
MultiValueMap<String, String> paramForPost02 = new LinkedMultiValueMap();
paramForPost02.add("key","value");
ResponseEntity<String> forPostEntity02 = restTemplate.postForEntity(urlForPost02, paramForPost02, String.class);
System.out.println("forPostEntity result :"+forPostEntity02.toString());
String forPostObject02 = restTemplate.postForObject(urlForPost02 ,paramForPost02, String.class);
System.out.println("forPostObject result :"+forPostObject02);
}
}
在Demo中每个接口我都以两种形式去调用即xxxForEntity()和xxxForObject()区别我们先通过,结果集打印的数据看一下差别。
getTemp01 getForEntity result :<200,getParam,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"8", Date:"Sat, 27 Jun 2020 02:11:09 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>
getTemp01 forObject result :getParam
------------------------------------------------------------------------------
postTemp01 forPostEntity result :<200,{param=getParam},[Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Sat, 27 Jun 2020 02:11:09 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>
postTemp01 forPostObject result :{param=getParam}
------------------------------------------------------------------------------
postTemp02 forPostEntity result :<200,value,[Content-Type:"text/plain;charset=UTF-8", Content-Length:"5", Date:"Sat, 27 Jun 2020 02:11:09 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>
postTemp02 forPostObject result :value
从打印结果上我们可以看出xxxForEntity()包含除返回结果外还包含返回的头信息,而xxxForObject()直接是将处理的结果返回给我们,丢掉了头中的信息。通过查看源码我们也可以发现,xxxForEntity()会多一步将结果的获取形式封装成Entity的方法。
至此简单的调用Demo就完成了。如果在调用的过程中需要在头信息中加入其他的参数我们也可以使用 RestTemplate API 中 exchange 和 execute 方法发送 GET,POST 请求,可以更加细粒度控制请求的行为,如 Header 信息,数据处理方式等。
@auther:zhankai
网友评论