learn-tech/专栏/Spring编程常见错误50例/21SpringRestTemplate常见错误.md
2024-10-16 06:37:41 +08:00

20 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        21 Spring Rest Template 常见错误
                        你好,我是傅健。

前面几节课,我们介绍了一个 Spring 微服务使用数据库过程中可能遇到的常见错误。而实际上,除了直接使用数据库外,使用其他微服务来完成功能也是一个常见的应用场景。

一般而言,微服务之间的通信大多都是使用 HTTP 方式进行的,这自然少不了使用 HttpClient。在不使用 Spring 之前,我们一般都是直接使用 Apache HttpClient 和 Ok HttpClient 等,而一旦你引入 Spring你就有了一个更好的选择这就是我们这一讲的主角 RestTemplate。那么在使用它的过程中会遇到哪些错误呢接下来我们就来总结下。

案例 1参数类型是 MultiValueMap

首先,我们先来完成一个 API 接口,代码示例如下:

@RestController public class HelloWorldController { @RequestMapping(path = "hi", method = RequestMethod.POST) public String hi(@RequestParam("para1") String para1, @RequestParam("para2") String para2){ return "helloworld:" + para1 + "," + para2; }; }

这里我们想完成的功能是接受一个 Form 表单请求,读取表单定义的两个参数 para1 和 para2然后作为响应返回给客户端。

定义完这个接口后,我们使用 RestTemplate 来发送一个这样的表单请求,代码示例如下:

RestTemplate template = new RestTemplate(); Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("para1", "001"); paramMap.put("para2", "002");

String url = "http://localhost:8080/hi"; String result = template.postForObject(url, paramMap, String.class); System.out.println(result);

上述代码定义了一个 Map包含了 2 个表单参数,然后使用 RestTemplate 的 postForObject 提交这个表单。

测试后你会发现事与愿违,返回提示 400 错误,即请求出错:

具体而言,就是缺少 para1 表单参数。为什么会出现这个错误呢?我们提交的表单最后又成了什么?

案例解析

在具体解析这个问题之前,我们先来直观地了解下,当我们使用上述的 RestTemplate 提交表单,最后的提交请求长什么样?这里我使用 Wireshark 抓包工具直接给你抓取出来:

从上图可以看出,我们实际上是将定义的表单数据以 JSON 请求体Body的形式提交过去了所以我们的接口处理自然取不到任何表单参数。

那么为什么会以 JSON 请求体来提交数据呢?这里我们不妨扫一眼 RestTemplate 中执行上述代码时的关键几处代码调用。

首先,我们看下上述代码的调用栈:

确实可以验证,我们最终使用的是 Jackson 工具来对表单进行了序列化。使用到 JSON 的关键之处在于其中的关键调用 RestTemplate.HttpEntityRequestCallback#doWithRequest

public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { super.doWithRequest(httpRequest); Object requestBody = this.requestEntity.getBody(); if (requestBody == null) { //省略其他非关键代码 } else { Class requestBodyClass = requestBody.getClass(); Type requestBodyType = (this.requestEntity instanceof RequestEntity ? ((RequestEntity)this.requestEntity).getType() : requestBodyClass); HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); MediaType requestContentType = requestHeaders.getContentType(); for (HttpMessageConverter messageConverter : getMessageConverters()) { if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter