RestTemplate介绍

  • spring3.0开始支持
  • Http请求工具
  • 该工具与springboot或springcloud无关
  • 提供常见的REST请求模版
  • 例如支持GET、PUT、POST、DELETE
  • 通用请求方法 --> exchange和execute
  • 实现RestOperations接口
  • 该接口定义了常见的RESTful操作

1.GET操作

首先在provider中定义一个 hello2 接口:

@GetMapping("/hello2")
    public String hello2(String name){
        return "hello "+name;
    }

接下来,我们在consumer去访问这个接囗,调用 RestTemplate中 的GET请求。 可以看到,在RestTemplate中,关于GET请求,一共有两大类方法:

这两大类方法实际上是重载的,唯一不同的,就是返回值类型。 getForObject返回的是一个对象,这个对象就是服务端返回的具体值。getForEntiey返回的是一个 ResponseEntity,这个ResponseEntity中除了服务端返回的具体数据外,还保留了Http应头的数据。

@GetMapping("/hello4")
    public void hello4(){
        String s1 = restTemplate.getForObject("http://provider/hello2?name={1}",String.class,"javaone");
        System.out.println(s1);
        ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://provider/hello2", String.class, "javaone");
        String body = responseEntity.getBody();
        System.out.println("body:"+body);
        HttpStatus statusCode = responseEntity.getStatusCode();
        System.out.println("HttpStatus:"+statusCode);
        int statusCodeValue = responseEntity.getStatusCodeValue();
        System.out.println("statusCodeValue:"+statusCodeValue);
        HttpHeaders headers = responseEntity.getHeaders();
        Set<String> keySet = headers.keySet();
        System.out.println("---------headers--------");
        for (String s : keySet) {
            System.out.println(s+":"+headers.get(s)+"///");
        }
    }

 这里大家可以看到,getForObject直接拿到了服务的返回值,getForEntity不仅仅拿到服务的返回值, 还拿到http应的状态码。然后,后动 Eureka Server、provider以及consumer,访问consumer 中的 hello4 接囗,既可以看到请求结果。

看清楚两者的区别之舌,接下来看下两个各自的重载方法,getForObject 和 getForEntity 分别有三个重载方法,两者的三个重载方法基本都是一致的。所以,这里,我们主要看其中一种。三个重载方法, 其实代表了三种不同的传参方式。

@GetMapping("/hello5")
    public void hello5(){
        String s1 = restTemplate.getForObject("http://provider/hello2?name={1}",String.class,"javatwo");
        System.out.println("1:"+s1);
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","zhangsan");
        s1 = restTemplate.getForObject("http://provider/hello2?name={name}", String.class,map);
        System.out.println("2:"+s1);
        try {
            String url = "http://provider/hello2?name="+ URLEncoder.encode("李四","UTF-8");
            URI uri = URI.create(url);
            s1 = restTemplate.getForObject(uri, String.class);
            System.out.println("3:"+s1);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }

小结:

  1. getForObject和getForEntity返回值区别:
  • getForObject返回一个对象(服务返回的具体值)
  • getForEntity不仅返回具体数据 还可以返回状态码 头信息...
  1. getForObject和getForEntity 三种重载方式的类似
  • Object... --> 占位符 (?xxx={1}, xx.class, "xxxxx")
  • Map<String, ?> --> 占位符为自定义key(name)需要提前声明map (?xxx={key}, xx.class, 返回的map)
  • URI --> 字符串中包含中文的需要转码才能创建为URI 在被调用

 

2.POST操作

2.1准备工作

因为post请求可能是k/v或是json形式,需要提供2种接口,传参对象需要创建一个model, 为了以后方便使用直接新建一个普通的maven项目作为commons模块管理。

创建一个User类。

@Data
@ToString
public class User {
    private Integer id;
    private String name;
    private String nickName;
}

然后provider和consumer分别引用commons模块依赖

<dependency>
  <groupId>com.example</groupId>
  <artifactId>commons</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

2.2 provider提供2个接口

// key/value形式传递
    @PostMapping("/user1")
    public User addUser1(User user){
        return user;
    }

    //json传递
    @PostMapping("/user2")
    public User addUser2(@RequestBody User user){
        return user;
    }

接下来,我们在consumer中调这两个POST接囗。

这里的post和前面的get很像,只是多出来了三个方法,就是postForLocation,另外,两个postForObject和postForEntiy和前面get基不一致,所以这里我们主要来看postForObject, 看完之后,我们再来看这个额外的postForLocation。

@GetMapping("/hello6")
    public void hello6(){
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("username","javajava");
        map.add("password","123456");
        map.add("id",666);
        User user = restTemplate.postForObject("http://provider/user1", map, User.class);
        System.out.println("1:"+user);

        user.setId(888);
        user = restTemplate.postForObject("http://provider/user2", user, User.class);
        System.out.println("2:"+user);
    }

post参数到底是k/v 形式还是json形式,主要看第二个参数,如果第二个参数是 MultiValueMap,则参数是以k/v形来传递的。 如果是一个普通对象,则参数是以的son形式 传递。

最后再看看一下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;
    }
}

 注意:

  1. 这里的post接口,响应一定是302,否则 postForLocaton 无效。
  2. 重定向的地址,一定要写成绝对路径,不要写成相对路径,否则在consumer 中调用时会出问题
@GetMapping("/hello7")
    public void hello7() {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("username", "javajava");
        map.add("password", "123456");
        map.add("id", 666);
        //这就是postForLocation,调用该方法返回的是一个Uri,Uri就是重定向的地址(里面包含重定向的参数),拿到Uri之后,就可以直接发送新的请求了
        URI uri = restTemplate.postForLocation("http://provider/register", map);
        String s = restTemplate.getForObject(uri, String.class);
        System.out.println(s);

    }

3.PUT 

 首先在provider提供一个put接口:

@PutMapping("/user11")
    public void updateUser1(User user1){ //k/v形式
        System.out.println(user1);
    }

    @PutMapping("/user12")
    public void updateUser2(@RequestBody User user2){ //json形式
        System.out.println(user2);
    }

在consumer中调用该接口,和上面post类似:

@GetMapping("/hello8")
    public void hello8() {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("username", "javajava");
        map.add("password", "123456");
        map.add("id", 666);
        restTemplate.put("http://provider/user11",map);
        User user = new User();
        user.setId(98);
        user.setUsername("zhangsan");
        user.setPassword("123456");
        restTemplate.put("http://provider/user12",user);
    }

4.DELETE 

 首先在provider提供一个delete接口:

@DeleteMapping("/user13")
    public void deleteUser1(Integer id){
        System.out.println(id);
    }

    @DeleteMapping("/user14/{id}")
    public void deleteUser2(@PathVariable Integer id){
        System.out.println(id);
    }

 在consumer中调用这两个删除接口(delete中参数的传递,也支持map,这块实际上和get是一样的):

@GetMapping("/hello9")
    public void hello9() {
        restTemplate.delete("http://provider/user13?id={1}",99);
        restTemplate.delete("http://provider/user14/{1}",99);
    }

 示例代码地址https://github.com/astronger/springcloud-simple-samples