解决前后端分离的项目传入null值变为"null"字符串的问题。
先说结论:这其实根本不是一个问题,而是事实。JSON传值的时候,null就会变成"null"字符串。
解决方式就是在前台加拦截器,把值为null的参数过滤掉。
一个Angular + SpringMVC的项目,当前台向后台传值的时候,如果传值为null,到后台会变为"null"。
前台发起请求的方法如下:
constructor(private httpClient: HttpClient) { } page(params: {clientId?: number, page: number, size: number, message?: string, level?: string}): Observable<Page<Log>> { const Params = { clientId: params.clientId ? params.clientId.toLocaleString() : null, page: params.page.toLocaleString(), size: params.size.toLocaleString(), message: params.message ? params.message : null, level: params.level ? params.level : null }; const url = '/log/page'; console.log(Params); return this.httpClient.get<Page<Log>>(url, {params: Params}); }
在前台会判断所有参数的值,如果为null或者undefined,参数就传null。
在浏览器控制台输出了请求的参数,就是实实在在的null:
后台被请求的方法如下:
@GetMapping("page") @JsonView(page.class) public Page<Log> page(@RequestParam(required = false) Long clientId, @RequestParam(required = false) String message, @RequestParam(required = false) String level, Pageable pageable) { logger.info(message+""+level); return new PageImpl(logService.page(clientId, level, message, pageable)); }
但是在后台判断空值的时候,意外的发现, 所有的空值判断都不生效 ,即使传入的是null,后台也不会按照null来处理,于是报错。
在后台打断点一看,才发现 所有的null传到后台之后,都变成了"null" :
null变成String,这就是空值判断失效的原因。
在解决问题之前,我们需要先明白,前台向后台传值,是使用JSON来完成的。
JSON : J ava S cript O bject N otation,JavaScript对象表示法。
虽然名字中有JS,但JSON并不是JavaScript专属,而是一种独立于语言的 文本格式 (类似markdown、XML之类的),因此,许多语言都可以解析JSON,它也就变成了不同语言之间传值的工具。
实际传值的时候,前台会把参数变成JSON字符串。
随便来一段JSON字符串,如下:
[ { "id": 1, "level": "DEBUG", "levelCode": 1, "logger": "123", "context": "1", "thread": "", "message": "123", "timestamp": "2020-03-15T21:53:32.000+0000", }, { "id": 2, "level": "INFO", "levelCode": 2, "logger": "123", "context": "2", "thread": "", "message": "123", "timestamp": "2020-03-10T21:53:32.000+0000" } ]
可以看出,JSON使用了键值对的形式,而值得关注的一点是,所有的信息,都是字符串或数字。
换句话说,JSON中只有字符串和数字。
由于数据传输时,是不能直接传对象的,所以在真正传值的时候,要转换成JSON字符串,无论什么类型都要转换成 字符串 或 数字 。
等到后台接收到参数的时候,其实后台也不知道传过来的是什么类型,只能按照规定好的参数类型,尝试对字符串进行强制转换。
于是,如果出现了null,前台就只能转化成"null",这就导致了后台只能接收到"null",甚至 无法区分 null和"null"。
更严重的问题在于:如果后台接收的参数是数字类型(Long、Int、Double),而前台传入是null的话,由于字符串强制转换为数字失败,会直接导致后台500.
总体思路,就是在前台加HTTP请求的拦截器,只要发现参数中有null,就把这个参数去掉。
由于发起请求时根本没有这个参数,后台接收到的就是实实在在的null了。
对于初学者来说,Angular设置拦截器可能比较困难,可以参考: 拦截器——SpringBoot+Angular入门实例教程
只需要在已经写好的负责拦截HTTP请求的拦截器中添加,并稍微改动:
/** * 过滤到null及undefined */ let cleanedParams = new HttpParams(); // request 需要替换成自己的 request.params.keys().forEach(x => { if (isDefined(request.params.get(x))) { cleanedParams = cleanedParams.append(x, req.params.get(x)); } }); request = request.clone({headers, params: cleanedParams});
当前台拦截器生效之后,再看控制台,就能发现,只剩下page和size两个参数了,其他的值为null的参数都被过滤掉了:
后台也确实变成null了:
至此,成功的过滤掉null参数。
前后台传值,null变成"null",这根本不算个问题,而是由于JSON的原理,只能变成字符串的形式。
解决方法就是在前台拦截器加入对空参数的过滤,过滤后不传这个参数,后台接收到的就是实实在在的null了。
通过这次解决问题,对拦截器和JSON有了更多的了解。