springboot 接口返回json中null转换成空数组或空字符串(递归+反射实现)

简介: 本来想加一个Jackson的配置类修改ObjectMapper实现把null转空字符串或空数组,但是加上之后发现没效果,找不到问题在哪里,不知道是不是我使用@RestControllerAdvice全局返回处理类的问题,所以就自己写了一个工具类方法处理,就在全局返回处理类里面调用

本来想加一个Jackson的配置类修改ObjectMapper实现把null转空字符串或空数组,但是加上之后发现没效果,找不到问题在哪里,不知道是不是我使用@RestControllerAdvice全局返回处理类的问题,所以就自己写了一个工具类方法处理,就在全局返回处理类里面调用


找到配置不生效的问题在哪里了,见springboot中添加Jackson配置类不生效


全局返回处理类是用kotlin写的,用来封装统一响应实体和处理全局异常的,用java也是一样的,语法换成java就行。当然,这不是这篇的博文重点,重点是处理null的方法


package com.gt.gxjhpt.configuration
import cn.dev33.satoken.exception.NotLoginException
import cn.dev33.satoken.exception.NotPermissionException
import cn.dev33.satoken.exception.NotRoleException
import cn.hutool.json.JSONUtil
import com.gt.gxjhpt.entity.RestfulResp
import com.gt.gxjhpt.enumeration.RespCodeEnum
import com.gt.gxjhpt.exception.MyException
import com.gt.gxjhpt.utils.MyUtils
import org.springframework.core.MethodParameter
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.http.server.ServletServerHttpResponse
import org.springframework.validation.BindException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
import javax.validation.ConstraintViolationException
@RestControllerAdvice
class GlobalResponseBodyAdvice : ResponseBodyAdvice<Any> {
    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
        return true
    }
    override fun beforeBodyWrite(
        body: Any?,
        returnType: MethodParameter,
        selectedContentType: MediaType,
        selectedConverterType: Class<out HttpMessageConverter<*>>,
        request: ServerHttpRequest,
        response: ServerHttpResponse
    ): Any? {
        return when (body) {
            is RestfulResp<*> -> body
            is String -> JSONUtil.toJsonStr(RestfulResp<Any>().success(body))
            null -> RestfulResp<Any>().success()
            else -> {
                val httpResponse = response as ServletServerHttpResponse
                if (httpResponse.servletResponse.status.equals(200)) {
                    // 设置null值为空字符串或空数组
                    MyUtils.setNullValue(body)
                    return RestfulResp<Any>().success(body)
                } else {
                    return body
                }
            }
        }
    }
    //其他参数异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = [BindException::class])
    fun handleBindException(): RestfulResp<*>? {
        return RestfulResp<Any>().enumResp(RespCodeEnum.PARAM_ERR)
    }
    //parameter异常
    //为了安全,就不将报错信息返回到前端,只返回粗略信息
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = [ConstraintViolationException::class])
    fun handleValidationException(): RestfulResp<*>? {
        return RestfulResp<Any>().enumResp(RespCodeEnum.PARAM_ERR)
    }
    //bean异常
    //为了安全,就不将报错信息返回到前端,只返回粗略信息
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = [MethodArgumentNotValidException::class])
    fun handleMethodArgumentNotValidException(ex: MethodArgumentNotValidException?): RestfulResp<*>? {
        return RestfulResp<Any>().enumResp(RespCodeEnum.PARAM_ERR)
    }
    //我的自定义异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(
        MyException::class
    )
    fun myException(e: MyException): RestfulResp<*>? {
        e.printStackTrace()
        return if (e.msg != null) {
            RestfulResp<Any>().error(e.msg, e.code)
        } else if (e.respCodeEnum != null) {
            RestfulResp<Any>().enumResp(e.respCodeEnum)
        } else {
            RestfulResp<Any>().unknown()
        }
    }
    //运行时异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(
        RuntimeException::class
    )
    fun runtimeException(e: RuntimeException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    //空指针异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(
        NullPointerException::class
    )
    fun nullPointerException(e: NullPointerException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown("空指针-->>" + e.stackTrace[0].toString())
    }
    //未知异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(
        Exception::class
    )
    fun exception(e: Exception): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    // 未提供token
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = [NotLoginException::class])
    fun exception(e: NotLoginException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    // 无角色
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(
        NotRoleException::class
    )
    fun exception(e: NotRoleException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
    // 无权限
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(
        NotPermissionException::class
    )
    fun exception(e: NotPermissionException): RestfulResp<*>? {
        e.printStackTrace()
        return RestfulResp<Any>().unknown(e.message)
    }
}


处理null的方法,不需要继续递归的类型,要写在第一个else if条件里面,工具类使用hutool


public static void setNullValue(Object body) throws IllegalAccessException, InstantiationException {
        Class<?> aClass = body.getClass();
        if (Collection.class.isAssignableFrom(aClass)) {
            for (Object it : Convert.toList(body)) {
                MyUtils.setNullValue(it);
            }
        } else if (String.class.isAssignableFrom(aClass) || NumberUtil.isNumber(body.toString())
                || BooleanUtil.isBoolean(aClass) || CharUtil.isChar(body) || Date.class.isAssignableFrom(aClass)) {
        } else if (Map.class.isAssignableFrom(aClass)) {
            Map<String, Object> objectMap = Convert.toMap(String.class, Object.class, body);
            objectMap.forEach((k, v) -> {
                try {
                    if (v == null) {
                        v = "";
                    } else {
                        MyUtils.setNullValue(v);
                    }
                } catch (IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
                objectMap.put(k, v);
            });
        } else { // 自定义响应对象
            List<Field> fields = CollUtil.toList(aClass.getDeclaredFields());
            // 父类属性
            Class<?> superclass = aClass.getSuperclass();
            while (superclass != null && superclass != Object.class) {
                fields.addAll(CollUtil.toList(superclass.getDeclaredFields()));
                superclass = superclass.getSuperclass();
            }
            for (Field field : fields) {
                // 取消权限检查
                field.setAccessible(true);
                Object value = field.get(body);
                if (value == null) {
                    if (Collection.class.isAssignableFrom(field.getType())) {
                        field.set(body, new ArrayList<>());
                    } else if (String.class.isAssignableFrom(field.getType())) {
                        field.set(body, "");
                    }
                } else {
                    MyUtils.setNullValue(value);
                }
            }
        }
    }
相关文章
|
8天前
|
SQL JSON Java
springboot 如何编写增删改查后端接口,小白极速入门,附完整代码
本文为Spring Boot增删改查接口的小白入门教程,介绍了项目的构建、配置YML文件、代码编写(包括实体类、Mapper接口、Mapper.xml、Service和Controller)以及使用Postman进行接口测试的方法。同时提供了SQL代码和完整代码的下载链接。
springboot 如何编写增删改查后端接口,小白极速入门,附完整代码
|
8天前
|
存储 前端开发 Java
springboot文件上传和下载接口的简单思路
本文介绍了在Spring Boot中实现文件上传和下载接口的简单思路。文件上传通过`MultipartFile`对象获取前端传递的文件并存储,返回对外访问路径;文件下载通过文件的uuid名称读取文件,并通过流的方式输出,实现文件下载功能。
springboot文件上传和下载接口的简单思路
|
23天前
|
存储 数据采集 Java
Spring Boot 3 实现GZIP压缩优化:显著减少接口流量消耗!
在Web开发过程中,随着应用规模的扩大和用户量的增长,接口流量的消耗成为了一个不容忽视的问题。为了提升应用的性能和用户体验,减少带宽占用,数据压缩成为了一个重要的优化手段。在Spring Boot 3中,通过集成GZIP压缩技术,我们可以显著减少接口流量的消耗,从而优化应用的性能。本文将详细介绍如何在Spring Boot 3中实现GZIP压缩优化。
66 6
|
1天前
|
存储 NoSQL Java
Spring Boot项目中使用Redis实现接口幂等性的方案
通过上述方法,可以有效地在Spring Boot项目中利用Redis实现接口幂等性,既保证了接口操作的安全性,又提高了系统的可靠性。
6 0
|
9天前
|
Java 网络架构
springboot配合thymeleaf,调用接口不跳转页面只显示文本
springboot配合thymeleaf,调用接口不跳转页面只显示文本
38 0
|
2月前
|
前端开发 小程序 Java
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
本文详细介绍了如何在SpringBoot项目中统一处理接口返回结果及全局异常。首先,通过封装`ResponseResult`类,实现了接口返回结果的规范化,包括状态码、状态信息、返回信息和数据等字段,提供了多种成功和失败的返回方法。其次,利用`@RestControllerAdvice`和`@ExceptionHandler`注解配置全局异常处理,捕获并友好地处理各种异常信息。
244 0
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
|
2月前
|
SQL Java 测试技术
SpringBoot单元测试快速写法问题之PorkService 接口中的 getPork 方法的作用如何解决
SpringBoot单元测试快速写法问题之PorkService 接口中的 getPork 方法的作用如何解决
|
2月前
|
小程序 JavaScript Java
微信小程序+SpringBoot接入后台服务,接口数据来自后端
这篇文章介绍了如何将微信小程序与SpringBoot后端服务进行数据交互,包括后端接口的编写、小程序获取接口数据的方法,以及数据在小程序中的展示。同时,还涉及到了使用Vue搭建后台管理系统,方便数据的查看和管理。
微信小程序+SpringBoot接入后台服务,接口数据来自后端
|
2月前
|
存储 监控 Java
|
2月前
|
JavaScript 前端开发 Java
SpringBoot 引入 smart-doc 接口文档管理插件,以及统一接口返回,最后推送到 Torna,进行统一管理
本文介绍了如何在SpringBoot项目中整合smart-doc接口文档管理插件,实现接口文档的生成和统一管理,并展示了如何将文档推送到Torna接口文档管理系统进行进一步的集中管理。
125 0
SpringBoot 引入 smart-doc 接口文档管理插件,以及统一接口返回,最后推送到 Torna,进行统一管理

热门文章

最新文章

下一篇
无影云桌面