# RestTemplate RestTemplate提供了比http client library更高层级的api,其允许以更加容易的方式来调用rest接口。 ## 初始化 RestTemplate的默认构造函数使用`java.net.HttpURLConnection`来执行http请求。 ## URIs RestTemplate的许多方法都接收一个URI template和URI tempalte变量要么作为`String`变量参数,要么作为`Map`. 如下示例使用了String类型参数: ```java String result = restTemplate.getForObject( "https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21"); ``` 如下示例则是使用了Map类型的参数: ```java Map vars = Collections.singletonMap("hotel", "42"); String result = restTemplate.getForObject( "https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); ``` URI tempalte是自动编码的,按如下示例所示: ```java restTemplate.getForObject("https://example.com/hotel list", String.class); // Results in request to "https://example.com/hotel%20list" ``` ## headers 可以通过`exchange()`方法来指定请求的headers,如下所示: ```java String uriTemplate = "https://example.com/hotels/{hotel}"; URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42); RequestEntity requestEntity = RequestEntity.get(uri) .header("MyRequestHeader", "MyValue") .build(); ResponseEntity response = template.exchange(requestEntity, String.class); String responseHeader = response.getHeaders().getFirst("MyResponseHeader"); String body = response.getBody(); ``` ## body 向RestTemplate中方法传入的参数或RestTemplate方法返回的对象,其与raw content之间的转换都是通过`HttpMessageConverter`来执行的。 在一个POST请求中,传入到RestTemplate方法中的参数会被序列化到请求的body中: ```java URI location = template.postForLocation("https://example.com/people", person); ``` **使用者并不需要显式设置请求体的`Content-Type`header**。在大多数情况下,都可以基于source Object类型找到一个合适的message converter,而被选中的message converter则会设置content type header。如果在必要情况下,可以通过`exchange`方法来显式提供`Content-Type` header,并且在设置content type请求头之后,会影响选中的message converter。 如下示例显示了一个get请求,将响应的body反序列化为方法的返回类型: ```java Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42); ``` 类似于`Content-Type`请求头,`Accept`请求头也不需要显式设置。在多数情况下,根据方法返回类型选中的message converter会帮助注入Accept请求头。在必要情况下,可以通过`exchange()`方法来指定要传递的`Accept`请求头。 默认情况下,RestTemplate注册了所有内置的message converter。 ## Multipart 如果需要发送multipart数据,需要提供一个`MultiValueMap`,该map的value可以是一个part content object,file part resource,含有part content和header的HttpEntity。 ```java MultiValueMap parts = new LinkedMultiValueMap<>(); parts.add("fieldPart", "fieldValue"); parts.add("filePart", new FileSystemResource("...logo.png")); parts.add("jsonPart", new Person("Jason")); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_XML); parts.add("xmlPart", new HttpEntity<>(myBean, headers)); ``` 当MultiValueMap构建好之后,可以将其传递给RestTemplate,如下所示: ```java MultiValueMap parts = ...; template.postForObject("https://example.com/upload", parts, Void.class); ``` 如果MultiValueMap至少含有一个不为String的值,那么Content-Type将会被`FormHttpMessageConverter`设置为`multipart/form-data`。 如果MultiValueMap中只有String类型的值,那么content-type会被设置为`application/x-www-form-urlencoded`。 ## Http接口 Spring允许将Http service定义为java接口,该方法上有注解用于http交换。可以通过该接口产生一个代理对象实现该接口,代理对象可以执行http请求交换。 首先,定义一个接口,并且通过`@HttpExchange`注解来标注接口中的方法: ```java interface RepositoryService { @GetExchange("/repos/{owner}/{repo}") Repository getRepository(@PathVariable String owner, @PathVariable String repo); // more HTTP exchange methods... } ``` 其次,可以创建一个代理对象来执行http请求,创建方式如下所示: ```java WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build(); HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build(); RepositoryService service = factory.createClient(RepositoryService.class); ``` `@HttpExchange`注解也支持类级别,类级别注解将会应用到接口中所有的方法: ```java @HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json") interface RepositoryService { @GetExchange Repository getRepository(@PathVariable String owner, @PathVariable String repo); @PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE) void updateRepository(@PathVariable String owner, @PathVariable String repo, @RequestParam String name, @RequestParam String description, @RequestParam String homepage); } ```