Claude Oscar Monet – Charing Cross Bridge 01
前后端分离开发的时候,后端怎么接收参数是个让人头疼的事情,因为方式不唯一,需要前后端统一,不然开发人员很难自己决定。为了节省篇幅,我尽量在一个请求中覆盖多种使用方式,我们可以这么写后端:
@PostMapping
现在需要模拟一个前端请求,以前我们用Postman(其实现在也在用),现在因为我们前端编辑器主要用VS Code,我们试试用它来发送请求,当然离不开插件。我们在VS Code中安装REST Client插件,然后创建一个后缀是.http的文件,在这个文件里直接写请求,写完点击发送就OK了,怎么写,官方有案例,下面是我们写的POST请求(为什么不用Postman了?你用Postman的话能一下把下面所有内容都复制过来吗?不能。但是在.http文件中可以):
POST
得到的结果,也会在VS Code中的右边窗口中展示:
HTTP/1.1 200
我们需要注意几点:
- 如果我们直接写诸如int age或者String name之类的话,其实默认和@RequestParam("age") int age或者@RequestParam("name") String name一样,只是前端接口参数名和形参名一致可以省略而已,而且@RequestParam和HttpServletRequest的getParameter一样只能从URL路径的?xxx=xxx&xxx=xxx中找匹配项。
- @PathVariable只能取对应路径位置上的那个值,而且不能省略,一省略那就变成上面第一种情况了。
- 如果设置Content-Type: application/json的话,body里面的数据只能通过@RequestBody注解取。json格式中的key和Model的属性不一致时,可在属性上使用@JsonAlias备注可接受的key名称(因为现在接口参数命名一般都是下划线格式,但我们属性一般是驼峰)。
今天提到这个,主要是因为在接口开发过程中,经常会遇到部分参数必选部分可选的情况,比如我们这里的实体类User,里面有三个属性:
public
我们使用这个类对象来接收参数的时候如果省略掉了age参数试试:
@RequestMapping
请求:
GET http://localhost:8080/getByModel?id=1&name=Jerry HTTP/1.1
结果发现age设置了默认值0:
User [name=Jerry, age=0, id=1]
好像也没什么大问题啊。那么我们再来换一种形式来接收参数。
@RequestMapping
发送同样缺少age参数的GET请求,这一次直接报错了:
{
我们把age参数补上,换成缺少id参数或者缺少name参数试试看,无论是使用哪种方式来接收参数,结果都没有报错,如果缺少的话,只是变成null值而没有报错:
User [name=null, age=10, id=null]
也就是说,这个世界好像对int数据类型充满了歧视。这个歧视还不是十分彻底,因为int类型的参数作为类的属性来接收时,它就不会报错,而是不存在时被初始化为0,就和Integer与String参数不存在时被初始化为null一样。但当它单打独斗时如果缺失对应的参数就报错了,没有像Integer和String那样强大到单打独斗时缺失仍然被初始化为null。
这个时候,就得看源码了。
一步步顺藤摸瓜可以找到org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver的resolveArgument方法,这个方法就是提取接口传过来的数据的:
@Override
这个处理null值的方法如下,看到下面那段话就明白了,它就是我们报错的那个信息,这里面主要是判断了是否是基本类型(boolean、byte、char、short、int、long、float、double),如果是,那直接报错,如果不是的话,其实最终还是返回这个value,value就是null,所以我们看到Integer等为空时被初始化为null了:
@Nullable
那么当基本数据类型不单打独斗放在类中当做属性接收参数,缺失时怎么就会被初始化了呢?这里面有个顺序的问题:本质的原因是我们先初始化了对象,对象里面的各个属性自然就会自动设置默认值,初始化之后,我们再从接口那边读数据,读到的话就赋值给对应的属性,读不到就Pass,于是就没有报错,而且基本类型数据如果缺失的话就是默认值。
如果是POST请求,数据放在body中的话,核心逻辑就在com.fasterxml.jackson.databind.deser.BeanDeserializer的deserialize方法。可以看到使用POST请求处理body里面的JSON数据时,默认走的是jackson的一套序列化和反序列化的东西。
@Override
如果是GET请求,数据放在URL后面,核心逻辑在org.springframework.web.method.annotation.ModelAttributeMethodProcessor的resolveArgument方法。
@Override
知道这个有什么用呢?尽量少用基本数据类型做形参,多用类对象做形参。如果非要使用基本类型数据的话,那就养成好习惯把写全一点,比如@RequestParam(name="age", required=false, defaultValue="0") int age。
再见。