[SpringBoot]-使用RestTemplate实现Restful接口客户端

参考:
https://blog.csdn.net/jinjiniao1/article/details/100849237
https://www.jianshu.com/p/90ec27b3b518

一、什么是RestTemplate

传统情况下在Java代码里访问restful服务,一般使用使用Java自带的HttpUrlConnection、或Apache的HttpClient。不过此种方法使用起来太过繁琐。Spring提供了一种简单便捷的模板类来进行操作,这就是RestTemplate。

RestTemplate是从Spring3.0开始支持的一个HTTP请求工具,它提供了常见的REST请求方案的模版,例如GET、POST、PUT、DELETE请求,以及一些通用的请求执行方法 exchange以及execute。
RestTemplate继承自InterceptingHttpAccessor并且实现了RestOperations接口,其中RestOperations接口定义了基本的RESTful操作,这些操作在RestTemplate中都得到了实现。

二、创建GET请求

RestTemplate类在org.springframework.web.client.RestTemplate;包中定义。

RestTemplate中的GET请求相关的方法有如下几个:

//getForObject方法
public <T> T getForObject(String URI, Class<T> responeType, Object... uriVariables) throws RestClientException {...}
public <T> T getForObject(String URI, Class<T> responeType, Map<String, ?> uriVariables) throws RestClientException {...}
public <T> T getForObject(URI URI, Class<T> responeType) throws RestClientException {...}

//getForEntity方法
public <T> ResponeEntity<T> getForEntity(String URI, Class<T> responeType, Object... uriVariables) throws RestClientException {...}
public <T> ResponeEntity<T> getForEntity(String URI, Class<T> responeType, Map<String, ?> uriVariables) throws RestClientException {...}
public <T> ResponeEntity<T> getForEntity(URI URI, Class<T> responeType) throws RestClientException {...}
  • getForEntity和getForObject都有三个重载方法,其重载方法的参数一样。方法的返回值都表示HTTP请求的返回值。
  • getForEntity和getForObject的差异:主要体现在返回值不同。RestTemplate 发送的是 HTTP 请求,响应消息中必然有响应头。
    • 如需要获取HTTP响应消息头,则需使用getForEntity()方法。getForEntity()此方法返回的是一个ResponseEntity实例,此实例中包含了响应头+响应数据。
    • getForObject()方法的返回值就是Restful接口服务方返回的的数据,无法获取到响应头。
  • GET 请求的参数只能在URI中传送。uriVariables参数就适用于设置URI中携带的参数。

1、getForEntity()方法使用

getForEntity()方法返回的值类型为ResponeEntity。其在org.springframework.http包中定义。其定义如下:

public class ResponeEntity<T> Extern HttpEntity<T> {
    private final Object status;
    
    public HttpStatus getStatusCode {...}
}
基类HttpEntity中定义了getBody()方法。
public class HttpEntity<T> {
    ....
    private final HttpHeaders headers;
    private final T Body;
    ...
    public HttpHeaders getHeaders() {...}
    public T getBody() {...}
}

以Restful接口为http://www.test.com/hello?name=%s为例。则Resful客户端使用getForEntity()方法的代码片段如下:

    public String hello(String name) {
        String url = "http://www.test.com/hello?name/hello?name={1}";
        
        ResponseEntity<String> responseEntity = RestTemplate.getForEntity(url, String.class, name);
        
        StringBuffer sb = new StringBuffer();
        HttpStatus statusCode = responseEntity.getStatusCode();
        String body = responseEntity.getBody();
        sb.append("statusCode:")
                .append(statusCode)
                .append("</br>")
                .append("body:")
                .append(body)
                .append("</br>");
        HttpHeaders headers = responseEntity.getHeaders();
        Set<String> keySet = headers.keySet();
        for (String s : keySet) {
            sb.append(s)
                    .append(":")
                    .append(headers.get(s))
                    .append("</br>");
        }
        return sb.toString();
    }

getForEntity 方法。

  • 第一个参数:是 url,url中有一个占位符 {1},如果有多个占位符分别用 {2} 、{3} … 去表示
  • 第二个参数:Restful接口返回的数据类型
  • 第三个参数:变长参数,用来给占位符填值。
    在返回的 ResponseEntity 实体中,可以获取响应头中的信息。其中:
  • getStatusCode 方法用来获取响应状态码;
  • getBody 方法用来获取响应数据;
  • getHeaders 方法用来获取响应头。

getForEntity()还有另外两种重载方法介绍:
1、第一个是占位符不使用数字,而是使用参数的 key。同时将参数放入到一个 map 中,map 中的 key 和占位符的 key 相对应,map 中的 value 就是参数的具体值。
样例:

    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello?name/hello?name={name}";
         
         Map<String, Object> map = new HashMap<>();
         map.put("name", name);
         
         ResponseEntity<String> responseEntity = RestTemplate.getForEntity(url, String.class, map);
         ...
    }

这种方式传参可能看起来更直观一些。

2、使用 Uri 对象,此时参数可以直接拼接在地址中
样例:

    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello?name/hello?name=“+URLEncoder.encode(name,"UTF-8");
         URI uri = URI.create(url);
         
         ResponseEntity<String> responseEntity = RestTemplate.getForEntity(uri, String.class);
         ...
    }

注意:这种传参方式,参数如果是中文的话,需要 URLEncoder.encode()方法对参数进行编码。

2、getForObject()方法使用

getForObject 方法和 getForEntity 方法类似。
样例

    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello?name/hello?name=“+URLEncoder.encode(name,"UTF-8");
         URI uri = URI.create(url);
         
         String str = restTemplate.getForObject(uri, String.class);
         ...
    }

注意:这里str就是Restful服务端返回的数据。如果开发者Restful接口返回的数据,不关系 HTTP响应头,那么可以使用getForObject()方法。

三、创建POST请求

GET 请求相比,RestTemplate中的 POST 请求多了一个类型的方法。其定义如下:

//psotForObject方法
public <T> T postForObject(String URI, Object request,Class<T> responeType, Object... uriVariables) throws RestClientException {...}
public <T> T getForObject(String URI,  Object request,Class<T> responeType, Map<String, ?> uriVariables) throws RestClientException {...}
public <T> T getForObject(URI URI,  Object request,Class<T> responeType) throws RestClientException {...}

//postForEntity方法
public <T> ResponeEntity<T> postForEntity(String URI, Object request, Class<T> responeType, Object... uriVariables) throws RestClientException {...}
public <T> ResponeEntity<T> postForEntity(String URI, Object request, Class<T> responeType, Map<String, ?> uriVariables) throws RestClientException {...}
public <T> ResponeEntity<T> postForEntity(URI URI, Object request, Class<T> responeType) throws RestClientException {...}

//psotForLocation方法
public URI postForLocation(String URI, Object request,Object... uriVariables) throws RestClientException {...}
public URI postForLocation(String URI,  Object request,Map<String, ?> uriVariables) throws RestClientException {...}
public URI postForLocation(URI URI, Object request) throws RestClientException {...}

**如上方法可以看出,Post 请求的参数可以在URI中传送,也可以通过消息Body传送:

  • uriVariables参数就适用于设置URI中携带的参数。
  • request参数设置的就是在HTTP请求的Body中携带的参数。**

1、postForEntity()方法

在POST请求中,当然可以可以通过URI传递参数(类似GET方法)

    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello?name/hello?name={1}";

         ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, null, String.class, name);
         return responseEntity.getBody();
    }

在 POST 请求中,也可以通过Body传递数据,数据可以是 key/value的形式、也可以是 JSON 格式。

使用的额是PostForEntity方法的request参数。

  1. 方法的request参数使用key/value形式
    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello";

         MultiValueMap map = new LinkedMultiValueMap();
         map.add("name", name);
         
         ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, map, String.class);
         return responseEntity.getBody();
         ...
    }

如上:

  • 第一个参数:请求地址;
  • 第二个参数 :请求参数。map 对象中存放着请求参数 key/value,RestTemplate会自动把参数转换为JSON; (详见后续章节”转换器说明“)
  • 第三个参数:HTTP响应数据被转换成的对象类型。
    当然,第一个参数也可以换成一个 URI 对象。
  1. 方法的request参数使用JSON形式
    以发送的JSON对应的Java类型是User,Restful服务端响应中也原样返回User类型的JSON字符串为例。
    User定义如下参考:
public class User {
    private String username;
    private String address;
    //省略getter/setter
}

postForEntity()方法调用参考:

    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello";

         User u1 = new User();
         u1.setUsername("TestName");
         u1.setAddress("shenzhen");
         ResponseEntity<User> responseEntity = restTemplate.postForEntity(url, u1, User.class);
         return responseEntity.getBody();
         ...
    }

如上:

  • 第一个参数:请求地址;
  • 第二个参数 :请求参数。RestTemplate会自动将一个对象转换成JSON进行传输。
  • 第三个参数:HTTP响应数据被转换成的对象类型。

2、postForObject()方法

postForObject 和 postForEntity 基本一致,就是返回类型不同而已,这里再赘述。

3、postForLocation()方法

POST 请求一般用来添加数据,但有时候需要将刚刚添加成功的数据的URL返回来,此时就可以使用这个方法。postForLocation 方法的返回值是一个 URI对象。
一个常见的使用场景如用户注册功能,用户注册成功之后,可能就自动跳转到登录页面了,此时就可以使用该方法。
样例:

  • Restful接口服务端提供用户注册功能 + 用户登录功能。如下:
...
    @RequestMapping("/register")
    public String register(User user) throws UnsupportedEncodingException     {
         return "redirect:/loginPage?username=" +     URLEncoder.encode(user.getUsername(),"UTF-8") + "&address=" + URLEncoder.encode(user.getAddress(),"UTF-8");
    }

   @GetMapping("/loginPage")
   @ResponseBody
   public String loginPage(User user) {
      return "loginPage:" + user.getUsername() + ":" + user.getAddress();
   }
...
  • 在Restful服务客户端调用注册接口。如下:
   @GetMapping("/Test")
   public String test() {
    List<ServiceInstance> list = discoveryClient.getInstances("provider");
    ...
    String url = "http://www.test.com/register";

    MultiValueMap map = new LinkedMultiValueMap();
    map.add("username", "TestName");
    map.add("address", "shenzhen");
    URI uri = restTemplate.postForLocation(url, map);
    String s = restTemplate.getForObject(uri, String.class);
    return s;
}

这里首先调用postForLocation()获取URI地址,然后再利用getForObject()方法请求新的URI。

注意:postForLocation() 方法返回的URI实际上是指响应头的Location字段。接口服务端的 register的响消息头中必须要有Location字段,否则postForLocation方法的返回值为null。

四、创建PUT请求

PUT 请求方法比较少,只有三个:

//put方法
public void put(String URI, Object request,Object... uriVariables) throws RestClientException {...}
public void put(String URI, Object request,Map<String, ?> uriVariables) throws RestClientException {...}
public void put(URI URI, Object request) throws RestClientException {...}

三个重载的方法其参数与 POST方法类似。

  • 第一个参数:请求地址;
  • 第二个参数:请求参数。可以是mao格式保存的 key/value,也可以是JSON格式。
  • 第三个参数:如果存在,则用于表示URI中占位服符。

**如上方法可以看出,put请求的参数可以在URI中传送,也可以通过消息Body传送:

  • uriVariables参数就适用于设置URI中携带的参数。
  • request参数设置的就是在HTTP请求的Body中携带的参数。**

样例:

    public String hello(String name) {
         ...
         String url = "http://www.test.com/hello";
         
         MultiValueMap map = new LinkedMultiValueMap();
         map.add("username", "TestName");
         map.add("address", "shenzhen");
         restTemplate.put(url, map);
          ...
    }

五、创建DELTE请求

DELETE请求也只有三个方法,如下:

//delete方法
public void delete(String URI, Object... uriVariables) throws RestClientException {...}
public void delete(String URI, Map<String, ?> uriVariables) throws RestClientException {...}
public void delete(URI URI) throws RestClientException {...}

DELETE 请求的参数只能在地址栏传送。

  • 可以是直接放在路径中。
  • 也可以用URI占位符方式。

样例:

    public String Delete(String name) {
         ...
         String url1 = "http://www.test.com/user/{id}";
         restTemplate.delete(url1, 1);
         ...
         
         String url2 = "http://www.test.com/user/?username={username}";
         Map<String,String> map = new HashMap<>();
         map.put("username", name);         
         restTemplate.delete(url2, map);
         ...
    }

六、exchange方法

在 RestTemplate 中还有一个通用的方法 exchange。其通用在于:它既能做 GET 请求,也能做 POST 请求,也能做其它各种类型的请求,这个方法需要你在调用的时候去指定请求类型。
如果开发者需要对请求进行封装,使用它再合适不过了,举个简单例子:

    public String hello(String name) {
         ...
         RestTemplate restTemplate = new RestTemplate();
         
         String url = "http://www.test.com/hello?name=“+URLEncoder.encode(name,"UTF-8");
         
         HttpHeaders headers = new HttpHeaders();
         headers.add("cookie","justdojava");
         HttpEntity<MultiValueMap<String,String>> request =  new HttpEntity<>(null,headers);
    
         ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
    
         System.out.println(responseEntity.getBody());
    }

七、手动指定转换器(HttpMessageConverter)

  • 前面可以看到,通过RestTemplate发送POST和PUT消息时,可以向方法传入“map格式的key/value”、或“Java对象实例”,方法自动把其转换成JSON字符串附在HTTP请求的Body中。
  • 同样,收到HTTP响应后,Body中也是JSON字符串。但是postForObject()方法的返回值也是Java对象。

RestTemplate是如何实现请求中把Java对象转换成JSON字符串,响应中把JSON字符串转换成Java对象呢?
这就涉及到转换器。

RestTemplate通过HttpMessageConverter自动帮我们做了转换的操作。

默认情况下,RestTemplate自动帮我们注册了一组HttpMessageConverter用来处理一些不同的contentType的请求。如:

  • StringHttpMessageConverter:来处理text/plain类型HTTP的body的转换;
  • MappingJackson2HttpMessageConverter:来处理application/json类型HTTP的body的转换;
  • MappingJackson2XmlHttpMessageConverter:来处理application/xml类型HTTP的body的转换。

可以在org.springframework.http.converter包下找到所有spring帮我们实现好的转换器。

如果默认的转换器不能满足需求,你也可以自定义转换器:实现org.springframework.http.converter.HttpMessageConverter接口。详情参考官方api。

实现好HttpMessageConverter后,如何把它注册到RestTemplate中呢?
以已经实现好一个名字叫GsonHttpMessageConverter的转换器为例。

    //创建RestTemplate实例
    RestTemplate restTemplate = new RestTemplate();
    
    //获取RestTemplate默认配置好的所有转换器
    List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
    //默认的MappingJackson2HttpMessageConverter在第7个 先把它移除掉
    messageConverters.remove(6);
    //添加上GSON的转换器
    messageConverters.add(6, new GsonHttpMessageConverter());

八、设置拦截器(如用于自定义请求头)

有的时候我们会有一些特殊的需求,如:模拟 cookie需自定义请求头。
此时我们可以使用拦截器(ClientHttpRequestInterceptor) 。

自定义拦截器需要我们自己实现org.springframework.http.client.ClientHttpRequestInterceptor接口。

样例:
实现一个拦截器,新增cookie消息头。

public class TestTokenInterceptor implements ClientHttpRequestInterceptor
{
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
    {
        HttpHeaders headers = request.getHeaders();
        headers.add("cookie","justdojava");
        
        return execution.execute(request, body);
    }
}

创建RestTemplate实例,并向其添加拦截器:

    public String hello(String name) {
         ...
         RestTemplate restTemplate = new RestTemplate();
         
         String url = "http://www.test.com/hello?name=“+URLEncoder.encode(name,"UTF-8");
         URI uri = URI.create(url);
         
         //向restTemplate中添加自定义的拦截器
         restTemplate.getInterceptors().add(new TestTokenInterceptor());
         
         //参考:也可把链接器设置为只有自定定义的
         //restTemplate.setInterceptors(Collections.singletonList(new TestTokenInterceptor());
         
         String str = restTemplate.getForObject(uri, String.class);
         ...
    }

九、设置底层连接方式

创建一个RestTemplate的实例,您可以像上述例子中简单地调用默认的无参数构造函数。这将使用java.net包中的标准Java类作为底层实现来创建HTTP请求。

但有些时候,我们需要像传统的HttpClient那样设置HTTP请求的一些属性。RestTemplate使用了一种很偷懒的方式实现了这个需求,那就是直接使用一个HttpClient作为底层实现。

样例:

    //生成一个设置了连接超时时间、请求超时时间、异常最大重试次数的httpClient
    RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(10000).setSocketTimeout(30000).build();
    
    HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(config).setRetryHandler(new DefaultHttpRequestRetryHandler(5, false));
    
    HttpClient httpClient = builder.build();
    //使用httpClient创建一个ClientHttpRequestFactory的实现
    ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
    
    //ClientHttpRequestFactory作为参数构造一个使用作为底层的RestTemplate
    RestTemplate restTemplate = new RestTemplate(requestFactory);
原文地址:https://www.cnblogs.com/yickel/p/14413389.html