收藏
回答

小程序手机号解析接口一直报 412 Precondition Failed: [no body]?

public String getPhoneNumber(String phoneCode) {
    JSONObject params = new JSONObject();
    params.put("code", phoneCode);
    String url = MessageFormat.format("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={0}", getAccessToken());
    JSONObject reObj = restTemplate.postForObject(url, params, JSONObject.class);
    if (reObj != null && reObj.containsKey("errcode")) {
        log.info(reObj.toJSONString());
        if (reObj.getInteger("errcode") == 0) {
            Code2PhoneNumberResponse code2PhoneNumberResponse = JSONObject.parseObject(reObj.toJSONString(), Code2PhoneNumberResponse.class);
            return code2PhoneNumberResponse.getPhoneInfo().getPurePhoneNumber();
        }
    }
    return null;
}
回答关注问题邀请回答
收藏

7 个回答

  • 文人仁
    文人仁
    2024-08-08

    今天也遇到了这个问题,折腾半天,终于定位到问题所在。先贴下自己找到的答案:

    大致意思就是新版本的Spring框架不再在请求头里面自动添加"Content-Length"了,缺少这个请求头,就是我们出错的原因。

    再说解决方法,提供3种思路供大家参考:

    方法1:不用RestTemplate了,换一个客户端,比如hutool里面的HttpRequest;

    public WechatPhoneResponse getUserPhoneNumber(String code) {
        WechatAccessTokenResponse accessTokenResponse = this.getAccessToken();
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" +
                accessTokenResponse.getAccessToken();
        String body = String.format("{\"code\":\"%s\"}", code);
        HttpResponse response = HttpRequest.post(url)
                .body(body)
                .timeout(30000)
                .execute();
        try {
            log.info("response body : {}", response.body());
            return objectMapper.readValue(response.body(), WechatPhoneResponse.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    

    方法2:自己手动计算content-length,加到headers里面去

    public WechatPhoneResponse getUserPhoneNumber(String code) {
        WechatAccessTokenResponse accessTokenResponse = this.getAccessToken();
        String accessToken = accessTokenResponse.getAccessToken();
        if (accessToken == null || accessToken.isEmpty()) {
            throw new RuntimeException("Failed to obtain access token");
        }
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
        Map<String, Object> body = Map.of("code", code);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 将请求体转换为字节数组,并计算它的长度
        String jsonBody;
        try {
            jsonBody = objectMapper.writeValueAsString(body);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        byte[] bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
        int contentLength = bodyBytes.length;
        headers.setContentLength(contentLength);
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        log.debug("Request URL: {}", url);
        log.debug("Request Body: {}", body);
        log.debug("Request Headers: {}", headers);
        try {
            return restTemplate.postForObject(url, requestEntity, WechatPhoneResponse.class);
        } catch (HttpClientErrorException e) {
            log.error("HTTP Status Code: {}", e.getStatusCode());
            log.error("Response Body: {}", e.getResponseBodyAsString());
            throw e;
    

    方法3:向最前面描述的那样,修改RestTemplate的配置,把缓冲加回去;

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = builder
                .setConnectTimeout(Duration.ofSeconds(15))
                .setReadTimeout(Duration.ofSeconds(15))
                .build();
        configureMessageConverters(restTemplate);
        return restTemplate;
    }
    
    2024-08-08
    有用 1
    回复
  • 良风有信
    良风有信
    2024-09-09

    参见楼下的解释,其实是微信会检查Content-Length参数,但Spring Framework 6.1以后请求不再设置该参数(可以参加SimpleClientHttpRequestFactory中的setBufferRequestBody说明);另外可以debug看一下请求报文中的{Transfer-Encoding: chunked}

    自己在http header中自己设置Content-Length就行了

    String body = JSONObject.toJSONString(params);
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.setContentLength(body.getBytes(StandardCharsets.UTF_8).length);
    HttpEntity entity = new HttpEntity<>(body, headers);
    ResponseEntity bResp = restTemplate.postForEntity(url, entity, String.class);
    


    2024-09-09
    有用
    回复
  • 深空丶
    深空丶
    2024-08-22

    这是因为从 Spring Framework 6.1开始,大多数 ClientHttpRequestFactory 实现在将请求体发送到服务器之前不再使用缓冲区。因此,对于某些内容类型(如 JSON) ,不再知道内容大小,也不再设置 Content-Llength 。如果希望像前面那样对请求主体进行缓冲,只需在 BufferingClientHttpRequestFactory 中包装正在使用的 ClientHttpRequestFactory即可

    2024-08-22
    有用
    回复
  • 文人仁
    文人仁
    2024-08-08

    今天也遇到了这个问题,折腾半天,终于定位到问题所在。先贴下自己找到的答案:

    大致意思就是新版本的Spring框架不再在请求头里面自动添加"Content-Length"了,缺少这个请求头,就是我们出错的原因。

    再说解决方法,提供3种思路供大家参考:

    方法1:不用RestTemplate了,换一个客户端,比如hutool里面的HttpRequest;

    public WechatPhoneResponse getUserPhoneNumber(String code) {
        WechatAccessTokenResponse accessTokenResponse = this.getAccessToken();
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" +
                accessTokenResponse.getAccessToken();
        String body = String.format("{\"code\":\"%s\"}", code);
        HttpResponse response = HttpRequest.post(url)
                .body(body)
                .timeout(30000)
                .execute();
        try {
            log.info("response body : {}", response.body());
            return objectMapper.readValue(response.body(), WechatPhoneResponse.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    

    方法2:自己手动计算content-length,加到headers里面去

    public WechatPhoneResponse getUserPhoneNumber(String code) {
        WechatAccessTokenResponse accessTokenResponse = this.getAccessToken();
        String accessToken = accessTokenResponse.getAccessToken();
        if (accessToken == null || accessToken.isEmpty()) {
            throw new RuntimeException("Failed to obtain access token");
        }
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
        Map<String, Object> body = Map.of("code", code);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 将请求体转换为字节数组,并计算它的长度
        String jsonBody;
        try {
            jsonBody = objectMapper.writeValueAsString(body);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        byte[] bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
        int contentLength = bodyBytes.length;
        headers.setContentLength(contentLength);
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        log.debug("Request URL: {}", url);
        log.debug("Request Body: {}", body);
        log.debug("Request Headers: {}", headers);
        try {
            return restTemplate.postForObject(url, requestEntity, WechatPhoneResponse.class);
        } catch (HttpClientErrorException e) {
            log.error("HTTP Status Code: {}", e.getStatusCode());
            log.error("Response Body: {}", e.getResponseBodyAsString());
            throw e;
    

    方法3:向最前面描述的那样,修改RestTemplate的配置,把缓冲加回去;

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = builder
                .setConnectTimeout(Duration.ofSeconds(15))
                .setReadTimeout(Duration.ofSeconds(15))
                .build();
        configureMessageConverters(restTemplate);
        return restTemplate;
    }
    
    2024-08-08
    有用
    回复
  • 文人仁
    文人仁
    2024-08-08

    今天也遇到了这个问题,折腾半天,终于定位到问题所在。先贴下自己找到的答案:

    大致意思就是新版本的Spring框架不再在请求头里面自动添加"Content-Length"了,缺少这个请求头,就是我们出错的原因。

    再说解决方法,提供3种思路供大家参考:

    方法1:不用RestTemplate了,换一个客户端,比如hutool里面的HttpRequest;

    public WechatPhoneResponse getUserPhoneNumber(String code) {
        WechatAccessTokenResponse accessTokenResponse = this.getAccessToken();
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" +
                accessTokenResponse.getAccessToken();
        String body = String.format("{\"code\":\"%s\"}", code);
        HttpResponse response = HttpRequest.post(url)
                .body(body)
                .timeout(30000)
                .execute();
        try {
            log.info("response body : {}", response.body());
            return objectMapper.readValue(response.body(), WechatPhoneResponse.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    

    方法2:自己手动计算content-length,加到headers里面去

    public WechatPhoneResponse getUserPhoneNumber(String code) {
        WechatAccessTokenResponse accessTokenResponse = this.getAccessToken();
        String accessToken = accessTokenResponse.getAccessToken();
        if (accessToken == null || accessToken.isEmpty()) {
            throw new RuntimeException("Failed to obtain access token");
        }
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
        Map<String, Object> body = Map.of("code", code);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 将请求体转换为字节数组,并计算它的长度
        String jsonBody;
        try {
            jsonBody = objectMapper.writeValueAsString(body);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        byte[] bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
        int contentLength = bodyBytes.length;
        headers.setContentLength(contentLength);
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        log.debug("Request URL: {}", url);
        log.debug("Request Body: {}", body);
        log.debug("Request Headers: {}", headers);
        try {
            return restTemplate.postForObject(url, requestEntity, WechatPhoneResponse.class);
        } catch (HttpClientErrorException e) {
            log.error("HTTP Status Code: {}", e.getStatusCode());
            log.error("Response Body: {}", e.getResponseBodyAsString());
            throw e;
    

    方法3:向最前面描述的那样,修改RestTemplate的配置,把缓冲加回去;

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = builder
                .setConnectTimeout(Duration.ofSeconds(15))
                .setReadTimeout(Duration.ofSeconds(15))
                .build();
        configureMessageConverters(restTemplate);
        return restTemplate;
    }
    
    2024-08-08
    有用
    回复
  • 山有扶苏
    山有扶苏
    2024-06-15

    试过spring boot 3.3.0、3.2.6和3.1.2,最终3.1.2是可以的,没有找到具体的原因,希望有大神能够解决。

    2024-06-15
    有用
    回复
  • 施小楠
    施小楠
    2024-06-13

    这里回复一下,不知道是springboot3的问题还是jdk17的问题,解决方案如下

    HashMap<String, Object> params = new HashMap<>();
    HttpHeaders headers = new HttpHeaders();
    params.put("code", phoneCode);
    HttpEntity<String> entity = new HttpEntity<>(JSON.toJSONString(params), headers);
    ResponseEntity<String> response = restTemplate.postForEntity(MessageFormat.format(phoneNumberUrl, getAccessToken()), entity, String.class);
    换成postForEntity,并且一定要是hashmap
    


    2024-06-13
    有用
    回复
登录 后发表内容