Spring RestTemplate发送x-www-form-urlencoded请求返回400错误求助
问题描述
使用Postman发送POST请求时可正常获得成功响应,但使用Spring RestTemplate发送相同请求时返回如下错误:
"httpCode": "400",
"httpMessage": "Bad Request",
"moreInformation": "One or more required API parameters are missing in the API request."
请求配置要求:Content-Type为application/x-www-form-urlencoded,Accept为application/json。
原有实现代码
HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); String clientConf = clientId + ":" +clientSecret; String base64ClientConf = Base64.getEncoder().encodeToString(clientConf.getBytes()); headers.set("Authorization","Basic "+ base64ClientConf); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("grant_type",this.grantType); map.add("scope",this.scopes); map.add("username",this.username); map.add("password",this.password); map.add("client_id",this.clientId); map.add("client_secret",this.clientSecret); URI uri = URI.create("url"); HttpEntity entity = new HttpEntity<>(map,headers); try { ResponseEntity<TokenResponse> queryResponseEntity = restTemplate.exchange(uri.toString(), HttpMethod.POST,entity, TokenResponse.class); TokenResponse queryResponses = queryResponseEntity.getBody(); System.out.println(queryResponses); } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { System.out.println("RestTemplate response Code = " + httpClientOrServerExc.getStatusCode() + " Hata = " + httpClientOrServerExc.getMessage()); throw new Exception("RestTemplate response Code = " + httpClientOrServerExc.getStatusCode() + " Hata = " + httpClientOrServerExc.getMessage()); }
Postman参考配置
- 请求Body配置:

- 请求Header配置:

问题根因
代码存在3个会触发服务端返回"缺少必填参数"错误的问题:
- 客户端凭证重复传递:已经通过
Authorization: Basic头传递了client_id和client_secret,又额外把这两个参数放到了form表单请求体中。绝大多数OAuth2接口不允许同时在请求头和请求体中传递客户端凭证,且参考的Postman配置仅在Basic Auth中传了这两个参数,没有在Body中重复添加,两边请求内容不一致。 - Base64编码未指定字符集:
clientConf.getBytes()会使用操作系统默认字符集编码字符串,在Windows等默认非UTF-8环境下,生成的Base64认证串会解析失败,服务端会判定未传合法的客户端凭证。 - 空参数序列化丢失:如果
grantType、scopes等字段存在null值,LinkedMultiValueMap在序列化form参数时会直接跳过null值项,导致必填参数缺失。
修复方案
- 移除form表单中冗余的
client_id、client_secret参数 - Base64编码时强制指定UTF-8字符集
- 提前校验所有必填参数非空,避免null值被跳过
修复后的核心代码:
// 按需导入java.nio.charset.StandardCharsets、java.util.Objects HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 指定UTF-8编码做Base64转换 String clientConf = clientId + ":" + clientSecret; String base64ClientConf = Base64.getEncoder().encodeToString(clientConf.getBytes(StandardCharsets.UTF_8)); headers.set("Authorization", "Basic " + base64ClientConf); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); // 提前校验参数非空,移除冗余的client_id、client_secret map.add("grant_type", Objects.requireNonNull(this.grantType, "grantType不能为空")); map.add("scope", Objects.requireNonNull(this.scopes, "scope不能为空")); map.add("username", Objects.requireNonNull(this.username, "username不能为空")); map.add("password", Objects.requireNonNull(this.password, "password不能为空")); URI uri = URI.create("实际请求地址"); HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers); try { ResponseEntity<TokenResponse> queryResponseEntity = restTemplate.exchange(uri, HttpMethod.POST, entity, TokenResponse.class); TokenResponse queryResponses = queryResponseEntity.getBody(); System.out.println(queryResponses); } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { System.out.println("RestTemplate response Code = " + httpClientOrServerExc.getStatusCode() + " Hata = " + httpClientOrServerExc.getMessage()); throw new Exception("RestTemplate response Code = " + httpClientOrServerExc.getStatusCode() + " Hata = " + httpClientOrServerExc.getMessage()); }
注:如果修复后仍然报错,可以在RestTemplate中开启请求日志,打印实际发出的form参数和头信息,和Postman的请求逐字段对比即可定位差异。
内容的提问来源于stack exchange,提问作者safa aytan
相关产品推荐
相关产品推荐

