使用RestTemplate调用POST接口返回403,但HttpEntity体非空
问题描述
使用RestTemplate调用需API-Key认证的POST接口时,持续收到403 Forbidden错误。相同授权头的GET请求可正常执行,说明API-Key有效;Postman调用该接口时,仅当不发送请求体才返回403,但代码中输出HttpEntity的体并非空值,推测是HttpEntity或restTemplate.exchange的使用有误,导致请求体未被正确发送。相关代码如下:
package at.acl.articledata.connector.smokeTest; import at.acl.articledata.connector.pojos.request.Article; import com.fasterxml.jackson.databind.util.JSONPObject; import io.swagger.v3.core.util.Json; import jakarta.annotation.PostConstruct; import org.apache.coyote.Response; import org.json.JSONArray; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.util.Collections; @Component public class SmokeTest { @Autowired RestTemplate restTemplate = new RestTemplate(); @PostConstruct private void floodDatabase(){ HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.add("X-API-KEY", "myApiKey"); JSONObject jsonArticle = new JSONObject(); jsonArticle.put("ext_article_nr", "e001"); jsonArticle.put("ean_numeric", BigDecimal.valueOf(420)); jsonArticle.put("intern_art_nr", "i001"); jsonArticle.put("article_name", "Flip Flops"); jsonArticle.put("size", "45"); jsonArticle.put("color", "Green"); jsonArticle.put("group", "No Group"); jsonArticle.put("brand", "Gucci"); jsonArticle.put("season", "Summer"); jsonArticle.put("article_link", "http://dummyimage.com/102x100.png/dddddd/000000"); JSONArray jsonArticleArray = new JSONArray(); jsonArticleArray.put(jsonArticle); JSONObject jsonBody = new JSONObject(); jsonBody.put("count", 1); jsonBody.put("total_count", 8); jsonBody.put("data", jsonArticleArray); HttpEntity<JSONObject> request = new HttpEntity<>(jsonBody, headers); ResponseEntity<String> response = restTemplate.exchange("correctServerIP", HttpMethod.POST, request, String.class); System.out.println("Post Response: " + response); } }
问题分析
- 消息转换器不兼容:RestTemplate默认使用
MappingJackson2HttpMessageConverter处理JSON请求体,但该转换器仅支持Jackson的ObjectNode或POJO,无法正确序列化org.json.JSONObject,导致请求体未被正确发送到服务器。 - Content-Type未显式设置:代码仅设置了
Accept头,但未指定Content-Type: application/json,服务器可能因无法识别请求体格式而拒绝请求。 - RestTemplate注入错误:同时使用
@Autowired和new RestTemplate(),可能导致Spring容器中的RestTemplate实例与代码中创建的实例不一致,影响消息转换器的配置。
解决方案
1. 替换JSON处理方式,使用Jackson构建请求体
改用Jackson的ObjectMapper来构建请求体,或者直接使用POJO(推荐),确保RestTemplate能正确序列化请求体。
2. 显式设置Content-Type头
在HttpHeaders中添加Content-Type: application/json,明确告知服务器请求体的格式。
3. 修正RestTemplate注入方式
移除@Autowired后的赋值语句,若需要自定义RestTemplate,通过@Bean注解在配置类中创建实例。
修改后的代码示例
package at.acl.articledata.connector.smokeTest; import at.acl.articledata.connector.pojos.request.Article; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.math.BigDecimal; import java.util.Collections; @Component public class SmokeTest { @Autowired private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; @PostConstruct private void floodDatabase(){ HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); // 显式设置Content-Type headers.setContentType(MediaType.APPLICATION_JSON); headers.add("X-API-KEY", "myApiKey"); // 使用Jackson构建请求体 ObjectNode jsonArticle = objectMapper.createObjectNode(); jsonArticle.put("ext_article_nr", "e001"); jsonArticle.put("ean_numeric", BigDecimal.valueOf(420)); jsonArticle.put("intern_art_nr", "i001"); jsonArticle.put("article_name", "Flip Flops"); jsonArticle.put("size", "45"); jsonArticle.put("color", "Green"); jsonArticle.put("group", "No Group"); jsonArticle.put("brand", "Gucci"); jsonArticle.put("season", "Summer"); jsonArticle.put("article_link", "http://dummyimage.com/102x100.png/dddddd/000000"); ArrayNode jsonArticleArray = objectMapper.createArrayNode(); jsonArticleArray.add(jsonArticle); ObjectNode jsonBody = objectMapper.createObjectNode(); jsonBody.put("count", 1); jsonBody.put("total_count", 8); jsonBody.set("data", jsonArticleArray); HttpEntity<ObjectNode> request = new HttpEntity<>(jsonBody, headers); ResponseEntity<String> response = restTemplate.exchange("correctServerIP", HttpMethod.POST, request, String.class); System.out.println("Post Response: " + response); } }
额外建议:使用POJO替代手动构建JSON
如果已有Article类,可以创建一个包含请求体结构的POJO类,代码会更简洁易维护:
// 新增请求体POJO public class ArticleBatchRequest { private int count; private int total_count; private List<Article> data; // Getter、Setter、构造方法 } // 修改floodDatabase方法 @PostConstruct private void floodDatabase(){ HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.setContentType(MediaType.APPLICATION_JSON); headers.add("X-API-KEY", "myApiKey"); Article article = new Article(); article.setExt_article_nr("e001"); article.setEan_numeric(BigDecimal.valueOf(420)); // 设置其他字段... ArticleBatchRequest requestBody = new ArticleBatchRequest(); requestBody.setCount(1); requestBody.setTotal_count(8); requestBody.setData(Collections.singletonList(article)); HttpEntity<ArticleBatchRequest> request = new HttpEntity<>(requestBody, headers); ResponseEntity<String> response = restTemplate.exchange("correctServerIP", HttpMethod.POST, request, String.class); System.out.println("Post Response: " + response); }
内容的提问来源于stack exchange,提问作者Splatted I0I
相关产品推荐
相关产品推荐

