Spring boot中feign使用GETPOST请求方式及@RequestParam,@RequestBody,@RequestPart的区别

Spring boot中feign使用GET/POST请求方式及@RequestParam,@RequestBody,@RequestPart的区别

使用@RequestPart,@RequestBody后 ,feign会使用post请求来访问远程客户端,并设置content-type为application/json,然后将@RequestPart/@RequestBody标识的参数以json格式放在body中,客户端接收需要使用@RequestBody进行接收,@RequestPart对应的 content-type为multipart/form-data或multipart/mixed stream

使用@RequestParam,无论是post还是get请求,@RequestParam标识的参数都会已queryString形式拼接在url后面,content-type为null,客户端接收需要使用@RequestParam进行接收

feign请求示例
	@RequestMapping(value = "/get_requestParam",method = RequestMethod.GET)
    Object get_requestParam(@RequestParam("accountId")String accountId);

    @RequestMapping(value = "/get_requestPart",method = RequestMethod.GET)
    Object get_requestPart(@RequestPart("accountId")String accountId);

    @RequestMapping(value = "/get_requestBody",method = RequestMethod.GET)
    Object get_requestBody(@RequestBody QueryAvailableTrialVoucherByUserParam param);

    @RequestMapping(value = "/post_requestParam",method = RequestMethod.POST)
    Object post_requestParam(@RequestParam("accountId") String accountId);

    @RequestMapping(value = "/post_requestPart",method = RequestMethod.POST)
    Object post_requestPart(@RequestPart("accountId") String accountId);

    @RequestMapping(value = "/post_requestBody",method = RequestMethod.POST)
    Object post_requestBody(@RequestBody QueryAvailableTrialVoucherByUserParam param);
客户端接口示例
	@GetMapping("/get_requestParam")
    public Object get_requestParam(@RequestParam("accountId") String accountId) {
        return "get_requestParam";
    }
	//requestPart 方式为post请求并需要使用@RequestBody接收
    @PostMapping("/get_requestPart")
    public Object get_requestPart(@RequestBody String accountId) {
        return "get_requestPart";
    }

    @PostMapping("/get_requestBody")
    public Object get_requestBody(@RequestBody QueryAvailableTrialVoucherByUserParam param) {
        return "get_requestBody";
    }

    @PostMapping("/post_requestParam")
    public Object post_requestParam(@RequestParam("accountId") String accountId) {
        return "post_requestParam";
    }
	//requestPart 需要使用@RequestBody接收
    @PostMapping("/post_requestPart")
    public Object post_requestPart(@RequestBody String accountId) {
        return "post_requestPart";
    }

    @PostMapping("/post_requestBody")
    public Object post_requestBody(@RequestBody QueryAvailableTrialVoucherByUserParam param) {
        return "post_requestBody";
    }

可能导致的问题

使用@RequestParam 发请求出现url长度过长问题

原因

无论是post还是get请求,@RequestParam标识的参数都会已queryString形式拼接在url后面,但是服务器对接收的url是有长度限制的,会截断后面的参数,致使请求失败

解决方法

  1. 修改 ymlproperties 配置文件
    server:
        port: 4450
        # 增加服务器请求头接受大小,URL 就是头部的一部分
        max-http-header-size: 10485760
    
  2. 把@RequestParam 的参数全部封装成一个对象,用@RequestBody 方式对该对象进行请求
使用@RequestBody 发请求出现 400 error Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t)

原因

传输的请求体body过大,开启了gzip压缩来传输请求,提升效率,而客户端content-type为 application/json,使用JSON的格式接受但无法转换gzip压缩后的内容,导致报错

解决方法

  1. 修改feign配置,关闭 请求压缩 或者 增大 最小请求阈值长度
    feign:
      compression:
      	# 请求是否开启gzip压缩
        request:
          enabled: false
          #最小请求阈值长度,超过该1024kb大小进行压缩
          min-request-size: 1024
        # 响应是否开启gzip压缩
        response:
          enabled: true
          # 响应解码
          useGzipDecoder: true
    
  2. 新增过滤器,专门处理Content-Encoding为gzip的请求

    MyGzipRequestWrapper

    public class MyGzipRequestWrapper extends HttpServletRequestWrapper {
    
        private HttpServletRequest request;
    
        public MyGzipRequestWrapper(HttpServletRequest request) {
            super(request);
            this.request = request;
        }
        
        @Override
        public ServletInputStream getInputStream() throws IOException {
    
            ServletInputStream inputStream = request.getInputStream();
            try {
                GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
                ServletInputStream newStream = new ServletInputStream() {
                    @Override
                    public boolean isFinished() {
                        return false;
                    }
    
                    @Override
                    public boolean isReady() {
                        return false;
                    }
    
                    @Override
                    public void setReadListener(ReadListener readListener) {
                    }
    
                    @Override
                    public int read() throws IOException {
                        return gzipInputStream.read();
                    }
                };
                return newStream;
            } catch (Exception e) {
            }
            return inputStream;
        }
    }
    

    MyGzipFilter

    public class MyGzipFilter implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String contentEncoding = request.getHeader("Content-Encoding");
            if (contentEncoding != null && !contentEncoding.isEmpty() && contentEncoding.contains("gzip")) {
                request = new MyGzipRequestWrapper(request);
            }
            System.out.println(request.getMethod());
            filterChain.doFilter(request, servletResponse);
        }
    }
    
  3. 使用原生的http,构造body去请求客户端的api