一、简介

RestTemplateSpring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。相较于之前常用的 HttpClientRestTemplate 是一种更优雅的调用 RESTful 服务的方式。

二、RestTemplate 用法

2.1 准备工作

创建一个用户实体类

public class User {

    private String username;
    
    public User(){}
    public User(String username){
        this.username = username;
    }
    
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

创建 Controller:

@RestController
public class UserController {

    private static final Logger log = LoggerFactory.getLogger(UserController.class);

    @GetMapping("/user/{username}")
    public User getUser(@PathVariable String username) {
        log.info("username :{}", username);
        return new User(username);
    }
    
    @PostMapping("/user")
    public User modify(String username){
        log.info("username :{}", username);
        return new User("modify");
    }
}

2.2 声明 RestTemplate

* Spring Boot <= 1.3 不需要定义,Spring Boot自动定义了一个
* Spring Boot >= 1.4 Spring Boot不再自动定义一个RestTemplate,而是定义了一个RestTemplateBuilder 可以更好地控制 RestTemplate 创建的对象
@Configuration
public class ApplicationConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

2.3 GET 请求

方法列表:

<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);

<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);

<T> T getForObject(URI url, Class<T> responseType);

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);

<T> ResponseEntity<T> getForEntity(URI var1, Class<T> responseType);

实例一:

getForObject 方法返回对象为响应体中数据转化成的对象

@SpringBootTest
class UserApiTests {

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void shouldGetRequestSuccess() {
        User expected = new User("MarkLogZhu");
        String url = "http://localhost:8900/user/{1}";
        User result = restTemplate.getForObject(url, User.class,"MarkLogZhu");
        assertTrue(expected.getUsername().equals(result.getUsername()));
    }

}

实例二:

getForEntity 方法返回对象为 ResponseEntity 对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等

@SpringBootTest
class UserApiTests {

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void shouldGetForEntityRequestSuccess() {
        User expected = new User("MarkLogZhu");
        String url = "http://localhost:8900/user/{1}";
        ResponseEntity<User> result = restTemplate.getForEntity(url,User.class,"MarkLogZhu");
        assertTrue(result.getStatusCode().is2xxSuccessful());
        assertTrue(expected.getUsername().equals(result.getBody().getUsername()));
    }
}

2.4 POST 请求

方法列表:

<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);

<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);

<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);

<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);

<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);

<T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType);

实例一:

getForObject 方法返回对象为响应体中数据转化成的对象

@SpringBootTest
class UserApiTests {

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void shouldPostForObjectRequestSuccess() {
        User expected = new User("modify");
        String url = "http://localhost:8900/user";
        Map<String, Object> map = new HashMap<>();
        map.put("username", "MarkLogZhu");
        User result = restTemplate.postForObject(url, map, User.class);
        assertTrue(expected.getUsername().equals(result.getUsername()));
    }

}

实例二:

getForEntity 方法返回对象为 ResponseEntity 对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等

@SpringBootTest
class UserApiTests {

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void shouldPostForEntityRequestSuccess() {
        User expected = new User("modify");
        String url = "http://localhost:8900/user";
        Map<String, Object> map = new HashMap<>();
        map.put("username", "MarkLogZhu");
        ResponseEntity<User> result = restTemplate.postForEntity(url, map, User.class);
        assertTrue(result.getStatusCode().is2xxSuccessful());
        assertTrue(expected.getUsername().equals(result.getBody().getUsername()));
    }
}

2.5 PUT请求方法

方法列表:

void put(String url, @Nullable Object request, Object... uriVariables);

void put(String url, @Nullable Object request, Map<String, ?> uriVariables);

void put(URI url, @Nullable Object request);

2.6 DELETE请求方法

方法列表:

void delete(String url, Object... uriVariables);

void delete(String url, Map<String, ?> uriVariables);

void delete(URI url);

三、RestTemplate 的组成

RestTemplate 包含如下几个部分:

* ClientHttpRequestFactory 客户端请求对象工厂
* ClientHttpRequestInterceptor 请求拦截器  
* HttpMessageConverter 对象转换器
* ResponseErrorHandler 异常处理

3.1 客户端请求对象工厂

ClientHttpRequestFactory 默认使用的是 JDKHttpURLConnection

@FunctionalInterface
public interface ClientHttpRequestFactory {
    
	ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}

public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
 	......
	@Override
	public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
		HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
		prepareConnection(connection, httpMethod.name());

		if (this.bufferRequestBody) {
			return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
		}
		else {
			return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
		}
	}        
    ......    
}

可以通过 setRequestFactory(ClientHttpRequestFactory requestFactory) 方法来更改请求实现。

3.2 请求拦截器

可以实现 ClientHttpRequestInterceptor 接口,自定义拦截器记录请求和响应头和主体:

public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        logRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        logResponse(response);
        return response;
    }


    private void logRequest(HttpRequest request, byte[] body) throws UnsupportedEncodingException {
        if (log.isDebugEnabled()){
            log.debug("===========================request begin================================================");
            log.debug("URI         : {}", request.getURI());
            log.debug("Method      : {}", request.getMethod());
            log.debug("Headers     : {}", request.getHeaders());
            log.debug("Request body: {}", new String(body, "UTF-8"));
            log.debug("==========================request end================================================");
        }
    }

    private void logResponse(ClientHttpResponse response) throws IOException {
        if (log.isDebugEnabled()){
            log.debug("============================response begin==========================================");
            log.debug("Status code  : {}", response.getStatusCode());
            log.debug("Status text  : {}", response.getStatusText());
            log.debug("Headers      : {}", response.getHeaders());
            log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
            log.debug("=======================response end=================================================");
        }
    }
}

注册拦截器

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    RestTemplate restTemplate = builder.build();
    restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
    // 默认的 ClientHttpRequestFactory 的响应只能被读取一次,会导致响应返回数据为空,可以使用 BufferingClientHttpRequestFactory ,它支持多次读取
      restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
    return restTemplate;
}

3.3 对象转换器

RestTemplate 的构造函数来看,它默认支持 ByteArrayHttpMessageConverterStringHttpMessageConverterResourceHttpMessageConverter 这三个转换器:

public RestTemplate() {
	this.messageConverters.add(new ByteArrayHttpMessageConverter());
	this.messageConverters.add(new StringHttpMessageConverter());
	this.messageConverters.add(new ResourceHttpMessageConverter(false));
	try {
		this.messageConverters.add(new SourceHttpMessageConverter<>());
	}
	catch (Error err) {
		// Ignore when no TransformerFactory implementation is available
	}
	this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

	if (romePresent) {
		this.messageConverters.add(new AtomFeedHttpMessageConverter());
		this.messageConverters.add(new RssChannelHttpMessageConverter());
	}

	if (jackson2XmlPresent) {
		this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
	}
	else if (jaxb2Present) {
		this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
	}

	if (jackson2Present) {
		this.messageConverters.add(new MappingJackson2HttpMessageConverter());
	}
	else if (gsonPresent) {
		this.messageConverters.add(new GsonHttpMessageConverter());
	}
	else if (jsonbPresent) {
		this.messageConverters.add(new JsonbHttpMessageConverter());
	}

	if (jackson2SmilePresent) {
		this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
	}
	if (jackson2CborPresent) {
		this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
	}

	this.uriTemplateHandler = initUriTemplateHandler();
}

也可以添加自定义的转换器,如微信接口返回是JSON格式的数据,但是响应头是Content-Type = text/plain,我们需要自定义一个转换器:

public class WeChatMessageConverter extends MappingJackson2HttpMessageConverter {

    public WeChatMessageConverter() {
        List<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.TEXT_PLAIN);
        setSupportedMediaTypes(mediaTypes);
    }

}

注册转换器:

restTemplate.getMessageConverters().add(new WeChatMessageConverter());