【小家Spring】Feign发送Get请求时,采用POJO对象传递参数的最终解决方案 Request method ‘POST‘ not supported (附带其余好几个坑)(上)

简介: 【小家Spring】Feign发送Get请求时,采用POJO对象传递参数的最终解决方案 Request method ‘POST‘ not supported (附带其余好几个坑)

前言


spring cloud技术栈里面,Feign可以使得我们的rest调用和调用本地方法一样方便。但是它真的有非常多的坑,苦不堪言啊。本文将描述我们最为常遇到的坑:


Feign发送Get请求时,采用POJO传递参数 Request method ‘POST’ not supported


坑 例举


Feign发送Get请求时,采用POJO传递参数的坑


在使用Feign client来调用Get请求接口时,如果方法的参数是一个对象,例如:


@FeignClient("microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/user", method = RequestMethod.GET)
  public PageBean<User> get(User user);
}


我们想得好好的。分页查询,查询条件用POJO的User对象进行包装进去。但奈何:在调试的时候你会一脸懵逼,因为报了如下错误:

feign.FeignException: status 405 reading UserFeignClient#get0(User); content:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed", "exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/user"}


what?老夫明明用的get请求啊,你竟然说Post方法不支持?


其实这个问题,在feign的github社区里面,一直有人提出了issue,只是一直没有被解决而已。


github上相关issue参考:


1.希望Feign能够支持参数请求使用POJO:https://github.com/spring-cloud/spring-cloud-netflix/issues/1253


2.解决办法:http://www.itmuch.com/spring-cloud-sum/feign-multiple-params/


3.建议使用Feign原生的注解的Issue:https://github.com/spring-cloud/spring-cloud-netflix/issues/659


4.建议增强Feign的功能:https://github.com/spring-cloud/spring-cloud-netflix/issues/1360


5.建议支持可选的Request Body(目前Feign当POST一个null时,会报异常):https://github.com/spring-cloud/spring-cloud-netflix/issues/1047


虽然可以采用@RequestParam的方式解决问题,但是很恼火的我,仔细想想:


你想写一堆长长的参数吗?用一个不知道里边有什么鬼的Map吗?或者转换为post?这似乎与REST风格不太搭,会浪费url资源,我们还需要在url定义上来区分Get或者Post。


于是就开始逐行调试,知道我从feign的源码中发现了这个:


sun.net.www.protocol.http;
public class HttpURLConnection extends java.net.HttpURLConnection {
  // 这里很简单:只要你doOutput = true了,你是get请求的话也会强制给你转为POST请求
  // 这倒不能说是JDK的bug,但是显然我不太喜欢这种处理方式~~~  你发现是get最多把我的body忽略就可以了呗
    private synchronized OutputStream getOutputStream0() throws IOException {
        try {
            if (!this.doOutput) {
                throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)");
            } else {
                if (this.method.equals("GET")) {
                    this.method = "POST";
                }
      }
      ...
  }
}


这段代码是在 HttpURLConnection 中发现的,jdk原生的http连接请求工具类,原来是因为Feign默认使用的连接工具实现类,所以里面发现只要你有body体对象,就会强制的把get请求转换成POST请求。


终上所述,这也不能怪feign,是HttpURLConnection 的问题。所以接下来我准备换一个HttpClient试试,因此本利我采用apache的HttpClient。但是一定,一定需要加入如下几个步骤:


 1.加入feign的配置项:feign.httpclient,enabled = true


 2.在依赖中引入apache的httpclient


<dependency>
 <groupId>org.apache.httpcomponents</groupId>
 <artifactId>httpclient</artifactId>
 <version>4.5.3</version>
</dependency>



 3.配置上此依赖(此依赖不可少 否则不生效的)


<!-- 使用Apache HttpClient替换Feign原生httpclient --> 
<dependency>
  <groupId>com.netflix.feign</groupId>
  <artifactId>feign-httpclient</artifactId>
  <version>${feign-httpclient}</version>
</dependency>


按照上面3个步骤添加好依赖后,我们可以很自由的使用User对象来传递get请求的参数了,是不是很优雅有木有。


但是一波三折,我发现服务端接受到的值都是null。因此我只能这么搞了


@FeignClient("microservice-provider-user")
public interface UserFeignClient {
 @RequestMapping(value = "/user", method = RequestMethod.GET)
 public PageBean<User> get(@RequestBody User user);
}


竟然在get请求里加上这么一个注解。结果,好使了。哈哈,完美



相关文章
|
3月前
|
缓存 监控 Java
《深入理解Spring》拦截器(Interceptor)——请求处理的艺术
Spring拦截器是Web开发中实现横切关注点的核心组件,基于AOP思想,可在请求处理前后执行日志记录、身份验证、权限控制等通用逻辑。相比Servlet过滤器,拦截器更贴近Spring容器,能访问Bean和上下文,适用于Controller级精细控制。通过实现`HandlerInterceptor`接口的`preHandle`、`postHandle`和`afterCompletion`方法,可灵活控制请求流程。结合配置类注册并设置路径匹配与执行顺序,实现高效复用与维护。常用于认证鉴权、性能监控、统一异常处理等场景,提升应用安全性与可维护性。
|
3月前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
6月前
|
前端开发 Java 数据库连接
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
|
6月前
|
JSON 前端开发 Java
Spring MVC 核心组件与请求处理机制详解
本文解析了 Spring MVC 的核心组件及请求流程,核心组件包括 DispatcherServlet(中央调度)、HandlerMapping(URL 匹配处理器)、HandlerAdapter(执行处理器)、Handler(业务方法)、ViewResolver(视图解析),其中仅 Handler 需开发者实现。 详细描述了请求执行的 7 步流程:请求到达 DispatcherServlet 后,经映射器、适配器找到并执行处理器,再通过视图解析器渲染视图(前后端分离下视图解析可省略)。 介绍了拦截器的使用(实现 HandlerInterceptor 接口 + 配置类)及与过滤器的区别
558 0
|
9月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
899 5
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
373 12
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
515 12
|
JSON 前端开发 Java
Spring MVC——获取参数和响应
本文介绍了如何在Spring框架中通过不同的注解和方法获取URL参数、上传文件、处理cookie和session、以及响应不同类型的数据。具体内容包括使用`@PathVariable`获取URL中的参数,使用`MultipartFile`上传文件,通过`HttpServletRequest`和`@CookieValue`获取cookie,通过`HttpSession`和`@SessionAttribute`获取session,以及如何返回静态页面、HTML代码片段、JSON数据,并设置HTTP状态码和响应头。
264 1
Spring MVC——获取参数和响应
|
JSON 前端开发 Java
Spring MVC——传递参数
本文介绍了在Spring框架中如何传递参数的方法,包括传递单个参数、多个参数、参数重命名、传递数组和集合以及JSON数据。对于单个参数,可以直接在方法中声明;多个参数无需关注传递顺序,只需确保参数名对应。使用`@RequestParam`注解可实现参数重命名,而传递数组和集合时需注意数据类型的转换。最后,通过`@RequestBody`注解可以处理JSON格式的数据,实现复杂对象的传递。
956 1
Spring MVC——传递参数
|
设计模式 前端开发 Java
Spring MVC——项目创建和建立请求连接
MVC是一种软件架构设计模式,将应用分为模型、视图和控制器三部分。Spring MVC是基于MVC模式的Web框架,通过`@RequestMapping`等注解实现URL路由映射,支持GET和POST请求,并可传递参数。创建Spring MVC项目与Spring Boot类似,使用`@RestController`注解标记控制器类。
195 1
Spring MVC——项目创建和建立请求连接