RestTemplate.postForObject调用第三方POST接口时请求体参数为空引发RestClientException的问题求助
大家好,我最近碰到个棘手的问题:我在一个GetMapping接口里,尝试用RestTemplate.postForObject()调用第三方的PostMapping登录接口,但每次都会抛出RestClientException,提示请求里的userName和password都是null。我用Postman直接调用这个第三方接口是完全正常的,能返回200响应,但代码调用就一直报错,麻烦各位帮忙看看问题出在哪?
我的代码示例
JSONObject responseObject = new JSONObject(); JSONObject requestObject = new JSONObject(); String url = "https://thirdpParty_API.com/api/auth/login"; try { requestObject.put("userName", "Tom"); requestObject.put("password", "Pass@12345"); responseObject = restTemplate.postForObject(url, requestObject , JSONObject.class); } catch(RestClientException rex) { logger.error("RestClientException occured : "+rex.getMessage()); return ResponseEntity.badRequest().build(); } return ResponseEntity.ok(responseObject);
第三方接口预期的请求体格式
{ "userName": "Tom", "password": "Pass@12345" }
抛出的异常信息
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 on POST request for "https://thirdpParty_API.com/api/auth/login": "{"type":"urn:problem-type:bad-request","title":"Bad Request","status":400,"detail":"Errors on loginRequestV1Dto: userName: must not be null, password: must not be null","arguments":[]}"
问题原因分析
Postman调用正常说明第三方接口本身没问题,问题出在代码的请求发送逻辑上:直接传递org.json.JSONObject给RestTemplate时,默认的消息转换器无法正确将其序列化为符合要求的JSON请求体,导致第三方接口接收不到有效参数,进而提示参数为空。
解决方案
下面提供几种可行的解决办法,按推荐程度排序:
方案1:创建对应Java实体类传递参数(最推荐)
创建一个和第三方接口请求体结构匹配的Java实体类,借助MappingJackson2HttpMessageConverter自动完成对象到JSON的序列化,代码可读性和维护性更高:
首先定义登录请求实体类:
public class LoginRequestDto { private String userName; private String password; // 全参构造方法 public LoginRequestDto(String userName, String password) { this.userName = userName; this.password = password; } // getter和setter方法(需补充完整) public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
修改调用代码:
JSONObject responseObject = new JSONObject(); String url = "https://thirdpParty_API.com/api/auth/login"; try { LoginRequestDto loginRequest = new LoginRequestDto("Tom", "Pass@12345"); // 直接传递实体类,RestTemplate会自动序列化为JSON请求体 responseObject = restTemplate.postForObject(url, loginRequest, JSONObject.class); } catch(RestClientException rex) { logger.error("RestClientException occured : "+rex.getMessage()); return ResponseEntity.badRequest().build(); } return ResponseEntity.ok(responseObject);
方案2:用HttpEntity封装请求体并指定Content-Type
将JSONObject转为字符串,通过HttpEntity封装请求体和请求头,明确指定Content-Type为application/json,确保请求格式正确:
JSONObject responseObject = new JSONObject(); JSONObject requestObject = new JSONObject(); String url = "https://thirdpParty_API.com/api/auth/login"; try { requestObject.put("userName", "Tom"); requestObject.put("password", "Pass@12345"); // 构造请求头,指定Content-Type HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // 封装请求体和请求头 HttpEntity<String> requestEntity = new HttpEntity<>(requestObject.toString(), headers); // 发送请求 responseObject = restTemplate.postForObject(url, requestEntity, JSONObject.class); } catch(RestClientException rex) { logger.error("RestClientException occured : "+rex.getMessage()); return ResponseEntity.badRequest().build(); } return ResponseEntity.ok(responseObject);
方案3:检查并配置RestTemplate的消息转换器
如果上述方法无效,可能是你的RestTemplate未配置MappingJackson2HttpMessageConverter,可以手动添加:
// 初始化RestTemplate并配置消息转换器 RestTemplate restTemplate = new RestTemplate(); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON)); restTemplate.getMessageConverters().add(converter);
配置完成后再执行请求,就能正确处理JSON的序列化和反序列化了。
内容来源于stack exchange

