diff --git a/spring/Spring Http Client/Spring RestTemplate.md b/spring/Spring Http Client/Spring RestTemplate.md new file mode 100644 index 0000000..a32b306 --- /dev/null +++ b/spring/Spring Http Client/Spring RestTemplate.md @@ -0,0 +1,111 @@ +# 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); + +} +``` + +