前言
解决前后端分离的项目传入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> {
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>(url, {params: Params});
}
在前台会判断所有参数的值,如果为null或者undefined,参数就传null。
在浏览器控制台输出了请求的参数,就是实实在在的null:
后台被请求的方法如下:
@GetMapping("page")
@JsonView(page.class)
public Page 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: Java Script Object Notation,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有了更多的了解。