一、问题描述
前段时间写一功能,需要调用外部接口,使用postman可以调用成功,但是通过代码去调用一直报406 Not Acceptable的错误。错误信息如下图:
调用的代码片段类似如下(下面代码只是调用的方式,为了不透露公司代码),将响应体转换为JSONObject类型:
package com.test.controller;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/1")
@ResponseBody
public JSONObject test1(String param1, String param2) {
String url = "http://XX.XXX.XXX.XXX/rest/main/XXX";
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
JSONObject dataJson = new JSONObject();
dataJson.put("param1", param1);
dataJson.put("param2", param2);
String param = dataJson.toJSONString();
formData.add("data", param);
log.info("url:{},formData:{}.", url, formData);
// 1
JSONObject result = restTemplate.postForObject(url, formData, JSONObject.class);
log.info("result:{}.", result);
return result;
}
}
二、定位问题
通过维基百科可以知道406状态码的含义
说白了就是,不管是前端调用后端接口得到数据还是跨服务间接口的调用,若被调用者返回的数据的格式与调用者期望得到的数据格式不符或不能转换为调用者想要转换的数据格式便会报406异常。
OK,知道了什么情况会出现406,回到代码中看有什么问题。代码中使用的是post请求,并将结果转换为JSONObject形式。那问题可能就出在返回给我的不是json的,导致转换成JSONObject时出错。
联系到提供该接口的同事看了下他们这个接口的代码,果然问题出在这。被调用接口部分代码如下图:
可以通过@RequestMapping注解中的produces属性设置响应体类型和字符编码,可以看到上面接口设置的响应体类型为纯文本的形式。虽然纯文本内容的格式是json形式,但不能直接使用别的类型来转换接收。需要用String类型来接收该返回值,然后再转成JSONObject类型数据。
三、解决问题
将上述代码1处改为如下即可,即先用String类型接收响应体,然后再转换为JSONObject类型对象。
// 1
String result = restTemplate.postForObject(url, formData, String.class);
JSONObject jsonObject = JSONObject.parseObject(result);
四、补充
接口文档中该接口的描述只有出参举例,没有出参类型,看着出参是json结构并结合我司响应体基本都是json就想当然的用JSONObject接数据,接口文档书写不规范害死人啊。
还有为什么同样请求该接口,postman就可以调用成功?
因为使用postman创建一个新的请求时,请求头中的Accept属性值为*/*,即可以接受所有媒体类型的数据,当然可以接受“text/plain"类型的数据。
还有一点就是,在使用postman调用成功时就已经显示响应体是纯文本格式了。
若响应体是JSON格式的,则该处应如下:
也怪自己粗心当时没注意,兜转半天。