最近项目中大量利用了Spring Cloud Feign来对接http接口,踩了不少坑,也发生了一些对RESTFUL接口设计的想法,特此一篇记录下。
SpringMVC的请求参数绑定机制
相识Feign汗青的伴侣会知道,Feign自己是Netflix的产物,Spring Cloud Feign是在原生Feign的基本长举办了封装,引入了大量的SpringMVC注解支持,这一方面使得其更容易被宽大的Spring利用者开箱即用,但也发生了不小的夹杂浸染。所以在利用Spring Cloud Feign之前,笔者先先容一下SpringMVC的一个入参机制。预设一个RestController,在当地的8080端口启动一个应用,用于吸收http请求。
@RestController public class BookController { @RequestMapping(value = "/hello") // <1> public String hello(String name) { // <2> return "hello " + name; } }
这个接口写起来很是简朴,但实际springmvc做了很是多的兼容,使得这个接口可以接管多种请求方法。
<1> RequestMapping代表映射的路径,利用GET,POST,PUT,DELETE方法都可以映射到该端点。
<2> SpringMVC中常用的请求参数注解有(@RequestParam,@RequestBody,@PathVariable)等。name被默认当做@RequestParam。形参String name由框架利用字节码技能获取name这个名称,自动检测请求参数中key值为name的参数,也可以利用@RequestParam(“name”)包围变量自己的名称。当我们在url中携带name参数可能form表单中携带name参数时,会被获取到。
POST /hello HTTP/1.1 Host: localhost:8080 Content-Type: application/x-www-form-urlencoded name=formParam
或
GET /hello?name=queryString HTTP/1.1 Host: localhost:8080
Feign的请求参数绑定机制
上述的SpringMVC参数绑定机制,各人应该都长短常熟悉的,但这一切在Feign中有些许的差异。
我们来看一个很是简朴的,可是实际上错误的接口写法:
//留意:错误的接口写法 @FeignClient("book") public interface BookApi { @RequestMapping(value = "/hello",method = RequestMethod.GET) String hello(String name); }
设置请求地点:
ribbon: eureka: enabled: false book: ribbon: listOfServers: http://localhost:8080
我们凭据写SpringMVC的RestController的习惯写了一个FeignClient,凭据我们的一开始的想法,由于指定了请求方法是GET,那么name应该会作为QueryString拼接到Url中吧?发出一个这样的GET请求:
GET /hello?name=xxx HTTP/1.1 Host: localhost:8080
而实际上,RestController并没有吸收到,我们在RestController一侧的应用中得到了一些提示:
查察文档发明,假如不加默认的注解,Feign则会对参数默认加上@RequestBody注解,而RequestBody必然是包括在请求体中的,GET方法无法包括。所以上述两个现象获得了表明。Feign在GET请求包括RequestBody时强制转成了POST请求,而不是报错。
领略清楚了这个机制我们就可以在开拓Feign接口制止许多坑。而办理上述这个问题也很简朴
Feign绑定复合参数
指定请求参数的范例与请求方法,上述问题的呈现实际上是由于在没有理清楚Feign内部机制的前提下想虽然的和SpringMVC举办了类比。同样,在利用工具作为参数时,也需要留意这样的问题。
对付这样的接口
@FeignClient("book") public interface BookApi { @RequestMapping(value = "/book",method = RequestMethod.POST) Book book(@RequestBody Book book); // <1> @RequestMapping(value = "/book",method = RequestMethod.POST) Book book(@RequestParam("id") String id,@RequestParam("name") String name); // <2> @RequestMapping(value = "/book",method = RequestMethod.POST) Book book(@RequestParam Map map); // <3> //错误的写法 @RequestMapping(value = "/book",method = RequestMethod.POST) Book book(@RequestParam Book book); // <4> }