fegin使用psot调用application/x-www-form-urlencoded类型请求传参414,413参数缺失错误等问题

问题描述:
第三方调用或者调用其他服务时,明确要求使用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,不然进入不了替换内容

至此上述三种方法都可以解决问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值