8

前言

解决前后端分离的项目传入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:
image.png

后台被请求的方法如下:

    @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"
image.png

null变成String,这就是空值判断失效的原因。

原因

在解决问题之前,我们需要先明白,前台向后台传值,是使用JSON来完成的。

JSONJavaScriptObjectNotation,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字符串,无论什么类型都要转换成字符串数字

等到后台接收到参数的时候,其实后台也不知道传过来的是什么类型,只能按照规定好的参数类型,尝试对字符串进行强制转换。

image.png

于是,如果出现了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的参数都被过滤掉了:

image.png

后台也确实变成null了:

image.png

至此,成功的过滤掉null参数。

总结

前后台传值,null变成"null",这根本不算个问题,而是由于JSON的原理,只能变成字符串的形式。

解决方法就是在前台拦截器加入对空参数的过滤,过滤后不传这个参数,后台接收到的就是实实在在的null了。

通过这次解决问题,对拦截器和JSON有了更多的了解。


LYX6666
1.6k 声望73 粉丝

一个正在茁壮成长的零基础小白