Spring应用消费REST服务
在微服务领域,使用Spring应用对另外一个应用的API发起请求的场景并不罕见。
Spring应用可以采用多张方式来消费REST API,包括以下几种方式
⒈RestTemplate
Spring核心框架提供的简单、同步REST客户端。
为了避免单调的样板代码,Spring提供了RestTemplate。RestTemplate提供了41个与REST资源交互的方法,下表中提供了常见的12方法。
方法 | 描述 |
delete(...) | 在特定的URL上对资源执行HTTP DELETE操作 |
exchange(...) |
在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity, 这个对象是从响应体中映射得到的 |
execute(...) | 在URL上执行特定的HTTP方法,返回一个从响应体映射得到的对象 |
getForEntity(...) |
发送一个HTTP GET请求,返回的ResponseEntity包含了响应体所 映射成的对象。 |
getForObject(...) | 发送一个HTTP GET请求,返回的请求体将映射为一个对象 |
headForHeaders(...) | 发送HTTP HEAD请求,返回包含特定资源URL的HTTP头信息 |
optionsForAllow(...) | 发送HTTP OPTIONS请求,返回特定URL的Allow头信息 |
patchForObject(...) | 发送HTTP PATCH请求,返回一个从响应体映射得到的对象 |
postForEntity(...) |
POST数据到一个URL,返回包含一个对象的ResponseEntity,这个 对象是从响应体中映射得到的 |
postForLocation(...) | POST数据到一个URL,返回新创建资源的URL |
postForObject(...) | POST数据到一个URL,返回根据响应体匹配形成的对象 |
put(...) | PUT资源到特定的URL |
除了TRACE以外,RestTemplate对每种标准的HTTP方法都提供了至少一个方法。除此之外,execute()和exchange()提供了较低层次的通用方法,以便于进行任意的HTTP操作。
1 @RestController 2 @Log 3 public class UserController { 4 5 private RestTemplate restTemplate = new RestTemplate(); 6 7 // ----------------- getForObject ----------------------- 8 public User getUserById(String userId){ 9 return restTemplate.getForObject("http://localhost:8080/user/{id}",User.class,userId); 10 } 11 12 public User getUserById2(String userId){ 13 Map<String,String> urlVariables = new HashMap<>(); 14 urlVariables.put("id",userId); 15 return restTemplate.getForObject("http://localhost:8080/user/{id}",User.class,urlVariables); 16 } 17 18 //构建Url参数的方式 19 public User getUserById3(String userId){ 20 Map<String,String> urlVariables = new HashMap<>(); 21 urlVariables.put("id",userId); 22 URI url = UriComponentsBuilder.fromHttpUrl("http://localhost:8080/user/{id}").build(urlVariables); 23 return restTemplate.getForObject(url, User.class); 24 } 25 26 // ----------------- getForEntity ----------------------- 27 // getForEntity()的工作方式和getForObject()类似,但是它返回的不是实体对象,而是一个包裹实体对象的ResponseEntity对象 28 // 借助ResponseEntity对象能够访问很多响应细节,比如响应头信息等。 29 public User getUserById4(String userId){ 30 ResponseEntity<User> responseEntity = restTemplate.getForEntity("http://localhost:8080/user/{id}",User.class, userId); 31 log.info("记录时间: " + responseEntity.getHeaders().getDate()); 32 return responseEntity.getBody(); 33 } 34 35 // ----------------- PUT ----------------------- 36 public void updateUser(User user){ 37 restTemplate.put("http://localhost:8080/user/{id}",user,user.getId()); 38 } 39 40 // ----------------- DELETE ----------------------- 41 public void delUser(User user){ 42 restTemplate.delete("http://localhost:8080/user/{id}",user.getId()); 43 } 44 45 // ----------------- POST ----------------------- 46 // 1.希望在POST请求后获得新添加的资源,使用postForObject() 47 public User createUser(User user){ 48 return restTemplate.postForObject("http://localhost:8080/user",user,User.class); 49 } 50 51 // 2.如果想要在创建资源后获得新资源的地址,使用postForLocation() 52 public URI createUser2(User user){ 53 return restTemplate.postForLocation("http://localhost:8080/user",user); 54 } 55 56 // 3.如果想要获取新添加的资源以及资源地址及更多信息,使用postForEntity() 57 public User createUser3(User user){ 58 ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://localhost:8080/user",user,User.class); 59 log.info("资源创建时间: " + responseEntity.getHeaders().getLocation()); 60 return responseEntity.getBody(); 61 } 62 }
⒉Traverson
Spring HATEOAS提供的支持超链接、同步的REST客户端,其灵感来源于同名的JavaScript库
当所消费的API在响应中包含了超链接,那么RestTemplate就力所不及了,我们可以使用RestTemplate获取详细的资源数据,然后手动创建逻辑来处理里面所包含的内容和链接。但这个过程并不简单,与其使用RestTemplate来处理超媒体API不如选择一个专门关注该领域的工具,那就是Traverson。
Traverson来源于Spring Data HATEOAS项目,是Spring应用中开箱即用的消费超媒体API的解决方案,这个基于Java的库灵感来源于同名的JavaScript库。
Traverson的名字有点类似于“tracerse on”,这种叫法其实可以很好地描述它的用法。它将会以遍历API关系名的方式来消费API。
1 @RestController 2 @Log 3 public class UserController { 4 5 private RestTemplate restTemplate = new RestTemplate(); 6 //使用API的基础URI来实例化一个Traverson对象 7 private Traverson traverson = new Traverson(URI.create("http://localhost:8080/api"), MediaTypes.HAL_JSON); 8 9 //获取用户列表 10 public Collection<User> getUsers(){ 11 ParameterizedTypeReference<CollectionModel<User>> userType = new ParameterizedTypeReference<CollectionModel<User>>() {}; 12 //通过follow()方法,就可以导航至链接关系名为users的资源,然后通过toObject()来提取资源。 13 CollectionModel<User> userRes = traverson.follow("users").toObject(userType); 14 Collection<User> users = userRes.getContent(); 15 return users; 16 } 17 18 //获取最近创建的用户 19 public Collection<User> getNewUsers(){ 20 ParameterizedTypeReference<CollectionModel<User>> userType = new ParameterizedTypeReference<CollectionModel<User>>() {}; 21 //CollectionModel<User> userRes = traverson.follow("users").follow("recents").toObject(userType); //可以简写 22 CollectionModel<User> userRes = traverson.follow("users","recents").toObject(userType); 23 Collection<User> users = userRes.getContent(); 24 return users; 25 } 26 27 //添加新用户 28 public User createUser(User user){ 29 //通过asLink()方法得到链接本身,然后getHref()得到链接的URL 30 String createUserUrl = traverson.follow("users").asLink().getHref(); 31 return restTemplate.postForObject(createUserUrl, user, User.class); 32 } 33 34 }
Traverson能够很容器地导航HATEOAS的API并消费其资源,但它并没有提供这些API写入或删除资源的方法。相反,RestTemplate能够写入和删除资源,但是在导航API方面支持得并不太好。
当既要导航API有需要更新或删除资源时,就需要组合使用RestTemplate和Traverson。Traverson仍然可以导航至创建新资源的链接。然后,可以将这个链接传递给RestTemplate来执行PUST、PUT、DELETE或任何其他需要的HTTP请求。【参见上面代码中的`添加新用户`节点】
⒊WebClient
Spring 5 所引入的反应式、异步REST客户端。【未完待续】