一、RestTemplate
RestTemplate 是从 Spring3.0 开始支持的一个 Http 请求工具,这个请求工具和 Spring Boot 无关,更和 Spring Cloud 无关。
RestTemplate 提供了常见的 REST 请求方法模板,例如 GET、POST、PUT、DELETE 请求以及一些通用的请求执行方法 exchange 和 execute 方法。
RestTemplate 本身实现了 RestOperations 接口,而在 RestOperations 接口中,定义了常见的 RESTful 操作,这些操作在 RestTemplate 中都得到了很好的实现
二、GET 方法
首先我们在 provider 中定义一个 hello2 接口:
@GetMapping("/hello2")
public String hello2(String name) {
return "hello " + name;
}
接下来,我们在 consumer 去访问这个接口,这个接口是一个 GET 请求,所以访问方式就是调用 RestTemplate 中的get 请求
可以看到,在 RestTemplate 中,关于 GET 请求,一共有如下两大类方法:
这两大类方法实际上是重载的,唯一不同的,就是返回值类型
getForObject
返回的是一个对象,这个对象就是服务端返回的具体值。
getForEntity
返回的是一个ResponseEntity
,这个ResponseEntity
中除了服务端返回的具体数据外,还保留了 Http 响应头的数据
@GetMapping("/hello4")
public void hello4() {
//使用 getForObject
String forObject = restTemplate.getForObject("http://provider/hello2?name={1}",
String.class, "Yolo");
System.out.println(forObject);
//使用 getForEntity,并获得其他属性
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://provider/hello2?name={1}",
String.class, "Yolo");
String body = forEntity.getBody();
System.out.println(body);
HttpStatus statusCode = forEntity.getStatusCode();
System.out.println(statusCode);
int statusCodeValue = forEntity.getStatusCodeValue();
System.out.println(statusCodeValue);
HttpHeaders headers = forEntity.getHeaders();
System.out.println("********* header *********");
for (String s : headers.keySet()) {
System.out.println(s+" : "+headers.get(s));
}
}
这里大家可以看到,getForObject 直接拿到了服务的返回值,getForEntity 不仅仅拿到服务的返回值,还拿到 http 响应的状态码。
然后,启动 Eureka Server、provider 以及 consumer ,访问 consumer 中的 hello4 接口,既可以看到请求结果。
看清楚两者的区别之后,接下来看下两个各自的重载方法,getForObject 和 getForEntity 分别有三个重载方法,两者的三个重载方法基本都是一致的。
所以,这里,我们主要看其中一种。三个重载方法,其实代表了三种不同的传参方式。
@GetMapping("/hello5")
public void hello5() throws UnsupportedEncodingException {
String forObject = restTemplate.getForObject("http://provider/hello2?name={1}", String.class, "Yolo");
System.out.println(forObject);
Map<String ,Object> map = new HashMap<>();
map.put("name","Yolo_map");
String s1 = restTemplate.getForObject("http://provider/hello2?name={name}", String.class, map);
System.out.println(s1);
String url = "http://provider/hello2?name="+ URLEncoder.encode("张三","UTF-8");
URI uri = URI.create(url);
String s = restTemplate.getForObject(uri, String.class);
System.out.println(s);
}
这就是所说的三种传参方式
三、POST 方法
首先在 provider 中提供两个 POST 接口,同时,因为 POST 请求可能需要传递 JSON,所以,这里我们创建一个普通的 Maven 项目作为 commons 模块,然后这个 commons 模块被 provider 和 consumer 共同引用,这样我们就可以方便的传递 JSON 了。
注意这里的模块需要创建为普通的 maven 模块,不可以创建为SpringBoot项目:具体原因点击参考
commons 模块创建成功后,首先在 commons 模块中添加 User 对象,然后该模块分别被 provider 和 consumer 引用。
然后,在 provider 中提供两个 post 接口:
@PostMapping("/user1")
public User addUser1(User user) {
return user;
}
@PostMapping("/user2")
public User addUser2(@RequestBody User user) {
return user;
}
这里定义了两个 User 添加的方法,两个方法代表了两种不同的传参方式。第一种方法是以 key/value
形式来传参,第二种方法是以 JSON 形式来传参。
定义完成后,接下来,我们在 consumer 中调用这两个 POST 接口。
可以看到,这里的 post 和前面的 get 非常像,只是多出来了三个方法,就是 postForLocation,另外两个 postForObject 和 postForEntiy 和前面 get 基本一致,所以这里我们主要来看 postForObject,看完之后,我们再来看这个额外的 postForLocation。
@GetMapping("/hello7")
public void hello7() {
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("username", "yolo");
map.add("password", "123");
map.add("id", 99);
User user = restTemplate.postForObject("http://provider/user1", map,
User.class);
System.out.println(user);
user.setId(98);
user = restTemplate.postForObject("http://provider/user2", user,
User.class);
System.out.println(user);
}
post 参数到底是 key/value
形式还是 json 形式,主要看第二个参数,如果第二个参数是 MultiValueMap ,则参数是以 key/value 形式来传递的,
如果是一个普通对象,则参数是以 json 形式 来传递的。
最后再看看一下 postForLocation 。
有的时候,当执行完一个 post 请求之后,立马要进行重定向,一个非常常见的场景就是注册,注册是一个 post 请求,注册完成之后,立马重定向到登录页面去登录。
对于这种场景,我们就可以使用 postForLocation。
首先,在 provider 上提供一个用户注册接口:
@Controller
public class RegisterController {
@PostMapping("/register")
public String register(User user) {
return "redirect:http://provider/loginPage?username=" +
user.getUsername();
}
@GetMapping("/loginPage")
@ResponseBody
public String loginPage(String username) {
return "loginPage:" + username;
}
}
注意,这里的 post 接口,响应一定是 302,否则 postForLocation 无效。
注意,重定向的地址,一定要写成绝对路径,不要写相对路径,否则在 consumer 中调用时会出问题
这就是 postForLocation ,调用该方法返回的是一个 Uri,这个 Uri 就是重定向的地址(里边也包含了重定向的参数),拿到 Uri 之后,就可以直接发送新的请求了。
四、PUT 方法
PUT 请求比较简单,重载的方法也比较少
我们首先在 provider 中提供一个 PUT 接口:
@PutMapping("/user3")
public void updateUser1(User user) {
System.out.println(user);
}
@PutMapping("/user4")
public void updateUser2(@RequestBody User user) {
System.out.println(user);
}
注意,PUT 接口传参其实和 POST 很像,也接受两种类型的参数,key/value
形式以及 JSON 形式。
在 consumer 中,我们来调用该接口:
@GetMapping("/hello9")
public void hello9() {
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("username", "yolo");
map.add("password", "123");
map.add("id", 99);
restTemplate.put("http://provider/user3", map);
User user = new User();
user.setId(98);
user.setUsername("zhangsan");
user.setPassword("456");
restTemplate.put("http://provider/user4", user);
}
consumer 中的写法基本和 post 类似,也是两种方式,可以传递两种不同类型的参数。
五、DELETE 方法
DELETE 也比较容易,我们有两种方式来传递参数,key/value 形式或者 PathVariable(参数放在路径中),首先我们在 provider 中定义两个 DELETE 方法:
@DeleteMapping("/user1")
public void deleteUser1(Integer id) {
System.out.println(id);
}
@DeleteMapping("/user2/{id}")
public void deleteUser2(@PathVariable Integer id) {
System.out.println(id);
}
然后在 consumer 中调用这两个删除的接口:
@GetMapping("/hello10")
public void hello10() {
restTemplate.delete("http://provider/user1?id={1}", 99);
restTemplate.delete("http://provider/user2/{1}", 99);
}
delete 中参数的传递,也支持 map,这块实际上和 get 是一样的。