Spring之在客户端访问RESTful业务 

RestTemplate 是客户端访问RESTful业务的核心类。在概念上与Spring其他的模板类相似,比如JdbcTemplate和JmsTemplate。RestTemplate的行为是可以定制的,通过提供回调方法和配置HttpMessageConverter(用于将对象打包到HTTP请求体中并从返回的响应中解压为一个对象)。由于一般使用XML作为消息格式,Spring提供了MarshallingHttpMessageConverter 使用对象到XML的框架,这是org.springframework.oxm包的一部分。这个给你了将XML映射为对象,提供了宽泛的选择。

 

这节描述了如何使用RestTemplate和其关联的HttpMessageConverters。

 

21.10.1 RestTemplate

Java中调用RESTful业务,一般使用一个辅助类,比如Apache的HttpComponents  HttpClient。对于普通的REST操作,这种方式是非常低级的。

[java] view plaincopy

1. String uri = "http://example.com/hotels/1/bookings";  
2.   
3. PostMethod post = new PostMethod(uri);  
4. String request = // create booking request content  
5. post.setRequestEntity(new StringRequestEntity(request));  
6.   
7. httpClient.executeMethod(post);  
8.   
9. if (HttpStatus.SC_CREATED == post.getStatusCode()) {  
10. "Location");  
11. if (location != null) {  
12. "Created new booking at :" + location.getValue());  
13.     }  
14. }

 

RestTemplate提供了高级方法,对应着六个主要的HTTP方法,使得在一行中调用RESTful业务并执行REST最好的实践。

 

注意:RestTemplate还有一个异步部分。

 

RestTemplate 方法回顾表

HTTP方法         RestTemplate方法
DELETE           delete
GET              getForObjectgetForEntity
HEAD             headForHeaders(String url,String。。。urlVariables)
OPTIONS          optionsForAllow(String url,String。。。urlVariables)
POST             postForLocation(String url,Object request,String。。。urlVariables)  postForObeject(String url,Object    request,Class<T> responseType,String...uriVariables)
PUT              put(String url,Object reuqest,String...urlVariables)
PATCH and others  exchange execute

RestTemplate方法的名字遵循一个命名规范,第一部分指定了调用什么HTTP方法,第二部分指明了返回值。例如,getForObject()方法执行一个GET,并将HTTP响应转为你想要的对象类型并返回这个对象。postForLocation()方法执行一个POST操作,将给定的对象转为一个HTTP请求并返回HTTP响应的location头,这里可以找到最新的对象。在处理HTTP请求的异常情形下,将抛出RestClientException异常类型;可以通过将ResponseErrorHandler实现插入到RestTemplate可以改变这种异常处理的行为。

 

exchange和execute方法是上述列表指定的方法的泛化版本并且支持其他的结合和方法,比如HTTP PATCH。然而,底层的HTTP也必须支持想得到的联合。JDK的HttpURLConnection 不支持PATCH方法,但是了Apache HttpComponents HttpClient的4.2版本或者后面的会支持。它们也使得RestTemplate读HTTP响应到一个一般的类型(比如List<Account>)),使用一个ParameterizedTypeReference,这是一个新类用来抓取和传递一般类型信息。

 

传递给这些方法的对象和从这些方法返回的对象由HttpMessageConverter实例转化和从消息转化。主要类型的转换器是默认注册的,但是了你也可以封装你自己的转换器并使用messageConverters()的bean属性注册。与这个模板注册的默认转换器实例是ByteArrayHttpMessageConverter,StringHttpMessageConverter,FormHttpMessageConverter和SourceHttpMessageConverter。你可以使用messageConverters()bean属性重写这些默认的,尤其是当使用MarshallingHttpMessageConverter或MappingJackson2HttpMessageConverter时重写就是必须的了。

 

每个方法传递URI模板参数,用两种方式,要不String变量长度参数,要不Map<String,String>. 例如:

 

[java] view plaincopy

 

    1. String result = restTemplate.getForObject(  
    2. "http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");

     

     

     

    上述使用的变量长度参数,

    [java] view plaincopy

    1. Map<String, String> vars = Collections.singletonMap("hotel", "42");  
    2. String result = restTemplate.getForObject(  
    3. "http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);  
    这使用的是Map<String,String>.

     

    为创建一个RestTemplate实例,你可以仅调用缺省的构造器。这将使用来自java.net包中的标准Java类,做实现HTTP请求的底层实现。这个可以通过指定ClientHttpRequestFactory实现来重写。Spring提供了HttpComponentsClientHttpRequestFactory 工厂类,使用Apache HttpComponents HttpClient  创建请求。HttpComponentsClientHttpRequestFactory使用org.apache.http.client.HttpClient的实例配置,反过来使用证书信息或者连接池功能配置。

     

    注意:

        java.net实现HTTP请求时,可能产生一个异常,当访问响应状态时,这代表了一个错误。如果这是一个问题,转为使用HttpComponentsClientHttpRequestFactory  

     

    前面的例子使用Apache HttpComponents HttpClient直接重写,并使用下面显示的RestTemplate:

    [java] view plaincopy

    1. uri = "http://example.com/hotels/{id}/bookings";  
    2.   
    3. RestTemplate template = new RestTemplate();  
    4.   
    5. Booking booking = // create booking object  
    6.   
    7. URI location = template.postForLocation(uri, booking, "1");


    为使用Apache HttpComponents 而不是本地的java.net功能,构造如下的RestTemplate :

    [java] view plaincopy

    1. RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

    注意:Apache HttpClient支持gzip编码。为使用它,构造了HttpComponentsClientHttpRequestFactory工厂类,如下所示:

     

    [java] view plaincopy

     

      1. HttpClient httpClient = HttpClientBuilder.create().build();  
      2. ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);  
      3. RestTemplate restTemplate = new RestTemplate(requestFactory);

       

      一般的回调接口是RequestCallback 并当调用执行方法时调用

       

      [java] view plaincopy

       

        1. public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,  
        2.         ResponseExtractor<T> responseExtractor, String... urlVariables)  
        3.   
        4. // also has an overload with urlVariables as a Map<String, String>.

         

         

        RequestCallback接口定义如下:

         

        [java] view plaincopy

         

          1. public interface RequestCallback {  
          2. void doWithRequest(ClientHttpRequest request) throws IOException;  
          3. }

          允许你操作请求头并写到请求体中。当使用execute方法时,你不必担心任何资源管理,模板将总是关闭请求并处理任何错误。

           

           


           

          使用URI working with URI

           

          对于每个主要的HTTP方法,RestTemplate提供了String类型的URI或者java.net.URI实例的变量作为第一个参数。

           

          String URI变量接受模板参数作为一个String变量长度参数或者作为一个Map<String,String>。它们也假设URL String类型是不编码的并且需要编码。例子如下:

          [java] view plaincopy

           

          1. restTemplate.getForObject("http://example.com/hotel list", String.class);

           

          将在http://example.com/hotel%20list上执行一个GET请求。这意味着如果你输入String类型的已经编码的URL,其将再编码一次。即http://example.com/hotel%20list将被变为http://example.com/hotel%2520list。如果这不是我们想要的结果,使用java.net.URI方法变量,其假设已经编码的url是有用的,如果你想多次使用一个单一的URI。


          UriComponentsBuilder这个类能用于构建和编码URI,包括支持URI模板。例如,你可以以一个String类型的url开始。


          [java] view plaincopy

          1. UriComponents uriComponents = UriComponentsBuilder.fromUriString(  
          2. "http://example.com/hotels/{hotel}/bookings/{booking}").build()  
          3. "42", "21")  
          4.         .encode();  
          5.   
          6. URI uri = uriComponents.toUri();

           

           

          或者分别指定每个URI组件:

           

          [java] view plaincopy

          1. UriComponents uriComponents = UriComponentsBuilder.newInstance()  
          2. "http").host("example.com").path("/hotels/{hotel}/bookings/{booking}").build()  
          3. "42", "21")  
          4.         .encode();  
          5.   
          6. URI uri = uriComponents.toUri();

           

           

          请求和响应头的处理

           

          除了上述的方法外,RestTemplate也有exchange()方法,能用于基于HttpEntity类的任何HTTP方法的执行。

           

          也许更加重要的是,exchange()这个方法可以用于添加请求和响应头。例如:

           

           

          [java] view plaincopy

          1. HttpHeaders requestHeaders = new HttpHeaders();  
          2. requestHeaders.set("MyRequestHeader", "MyValue");  
          3. HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);  
          4.   
          5. HttpEntity<String> response = template.exchange(  
          6. "http://example.com/hotels/{hotel}",  
          7. class, "42");  
          8.   
          9. String responseHeader = response.getHeaders().getFirst("MyResponseHeader");  
          10. String body = response.getBody();

           

          在上述的例子中,我们首先准备了一个请求,其包含了MyRequestHeader头信息。稍后我们检索了对应的响应,并读了MyResponseHeader和消息体。

           

          Jackson JSON 视图支持

          可能指定一个Jackson JSON View序列化对象属性子集。例如:

           

          [java] view plaincopy

          1. JacksonSerializationValue jsv = new JacksonSerializationValue(new User("eric", "7!jd#h23"),  
          2. class);  
          3. HttpEntity<JacksonSerializationValue> entity = new HttpEntity<JacksonSerializationValue>(jsv);  
          4. String s = template.postForObject("http://example.com/user", entity, String.class);