问题描述:
第三方调用或者调用其他服务时,明确要求使用application/x-www-form-urlencoded类型请求,并且要求参数放入请求body中
其中 服务器返回414,413都是因为参数直接拼接到请求url的后面,参考get请求方式,https://editor.youkuaiyun.com/md?not_checkout=1&spm=1&articleId=142519179
调试建议:在fegin请求时要开启调用日志方便问题排查
解决方案:
1、服务器修改接收参数的大小,一般调整nginx或者网关类的前置配置,但是调大后会影响服务器性能,一般情况对方也不会理会这种解决方式。治标不治本
2、调整参数从url放入请求体中
正常的请求使用@RequestParam传参,PostMapping设置application/x-www-form-urlencoded
@RequestParam传参格式在fegin调用的时候会自动拼接到url上去
数据量小时如所谓,如果你的参数特别大比如说超过8k服务器就会报错414,413等错误
@FeignClient(value = "startWorkflow2",url = "")
@Component
public interface StartWorkflow2Proxy {
@PostMapping(value = "",consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
JSONObject startWorkflow2(@RequestHeader("Authorization") String token, @RequestParam("id") String id,@RequestParam("parameters") String parameters);
//JSONObject startWorkflow2(@RequestHeader("Authorization") String token,@RequestBody StartWorkflowReq params);
}
那么这种情况下就只能将url的参数拿掉放入body中
方案是修改参数类型@RequestBody
JSONObject startWorkflow2(@RequestHeader("Authorization") String token,@RequestBody Map<String,String> params);
这时候请求发送参数也在body里了,记住这个Content-Length = 320
这是用map传参的长度。这个时候对方服务器给我们返回了参数缺失的错误。这时候肯定就要问了我都放进body了为啥还不对了,这种情况八成是对方使用的对象去接的,如下:
{
StartWorkflowReq startWorkflowReq = new StartWorkflowReq();
String parameters2 = "{\"apasPostXml\":\"\",\"attrXml\":\"\",\"billInfoXml\":\"\",\"formXml\":\"\",\"projid\":\"smdtGD002406240923000004\"}";
String id = "fce4d1b0eb2c997c1bd099ebf1ad4433";
startWorkflowReq.setId(id);
startWorkflowReq.setParameters(parameters2);
startWorkflow2Proxy.startWorkflow2("Bearer "+token.getAccess_token(),startWorkflowReq);
}
@PostMapping(value = "",consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@ResponseBody
JSONObject startWorkflow2(@RequestHeader("Authorization") String token,@RequestBody StartWorkflowReq params);
调用结果
看一下是不是两次调用时body的大小是不一样的。
所以这里需要注意一下,如果遇见参数缺失错误就要换一种传参体去试一下
总体逻辑是都是放入body中,只是对象需要序列化,map不需要,两者的长度不一致不能等价
3、第三种方法也可以解决
重写fegin拦截器在请求前重新包装请求体,
这种适应很多的application/x-www-form-urlencoded都采用的是@RequestParam
如果要是改成body代码改动多,工作量大还容易出问题遗漏
方法如下
/**
* Feign调用配置
*/
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor customRequestInterceptor() {
return new CustomRequestInterceptor();
}
@Component
public static class CustomRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 针对application/x-www-form-urlencoded类型的进行处理
if (template.method().equals("POST")) {
Collection<String> contentTypeValues = template.headers().get("Content-Type");
if (contentTypeValues != null && !contentTypeValues.isEmpty()) {
// 获取第一个值
String contentType = contentTypeValues.iterator().next();
if (contentType.equals(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
convertQueriesToBody(template);
}
} else {
System.out.println("Content-Type header is missing.");
}
}
}
private void convertQueriesToBody(RequestTemplate template) {
try {
byte[] body = template.body();
Map<String, Collection<String>> queries = template.queries();
if ((body == null || body.length == 0) && !queries.isEmpty()) {
StringBuilder bodyContent = new StringBuilder();
for (Map.Entry<String, Collection<String>> entry : queries.entrySet()) {
String key = entry.getKey();
Collection<String> values = entry.getValue();
for (String value : values) {
bodyContent.append(key).append("=").append(value).append("&");
}
}
// 移除最后多余的 '&'
if (bodyContent.length() > 0) {
bodyContent.setLength(bodyContent.length() - 1);
}
// 设置到 body
template.queries(null);
template.body(bodyContent.toString());
}
} catch (Exception e) {
System.out.println("Error converting queries to body: " + LogUtils.getStackTrace(e));
}
}
}
}
思路如下:重写拦截器,获取请求是post并且是application/x-www-form-urlencoded的请求,判断一下body是否有值,没有值的情况下去emplate.queries里面取@RequestParam的参数值,将url的值重新赋值给body,手动改造请求体,再将emplate.queries滞空。
注意:这里需要在PostMapping指定是application/x-www-form-urlencoded,不然进入不了替换内容
至此上述三种方法都可以解决问题