zoukankan      html  css  js  c++  java
  • [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);
    
  • 相关阅读:
    WCF简介-01
    专访Bruce Douglass,谈嵌入式经验
    svn查看工程版本库的url地址
    Hello Kiki (中国剩余定理模版)
    中国剩余定理 (CRT)
    Prime Cuts(POJ-1595)
    素数区间筛法
    出现或者反转后出现在每个字符串中的最长子串
    每个字符串至少出现两次且不重复的最长子串
    不小于k个字符串的最长子串
  • 原文地址:https://www.cnblogs.com/yickel/p/14413389.html
Copyright © 2011-2022 走看看