Json过滤器(基于spring自定义注解的方式,对字段进行过滤,支持嵌套类型,并接口响应体格式统一封装)

在实际的开发过程中,会经常遇到如下情景:

  • 后台需要给前端返回JSON数据,但是查询出来返回的数据里面有很多属性是不需要的

本文通过自定义注解的方式进行实现,对response进行拦截,通过注解参数,设置字段信息(即,过滤哪些字段,保留哪些字段),并将bean自动封装为json,作为结果返回。
实现的具体代码可以在github中直接下载运行:https://github.com/MonkeyJJC/JsonFilter

整体思路:
(1)通过ResponseBodyAdvice实现在响应体写出之前做一些处理;比如,修改返回值、加密等(此处即进行参数解析,设置过滤器的相关参数,为后续消息转换器处理准备)
(2)自定义消息转换器HttpMessageConverter
(3)自定义消息转换器的配置,通过@Bean定义HttpMessageConverter是向项目中添加消息转换器,如果Spring扫描到HttpMessageConverter类型的bean,就会将它自动添加到调用链中。否则spring会使用默认的处理器

Controll层:

@RestController
public class Demo {

    @GetMapping("user")
    @SerializeField(clazz = User.class, includes = {"name", "id"})
    public User user() {
        User user = new User(1L, "jjc", "123456");
        return user;
    }
}

其中@SerializeField就是自定义的注解,通过注解的方式声明是对User类进行过滤操作,留下的字段是{“name”, “id”},先看一下实现效果:

{
	id: 1,
	name: "jjc"
}

注解定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SerializeField {

    Class clazz();

    /**
     * 需要返回的字段
     * @return
     */
    String[] includes() default {};

    /**
     * 需要去除的字段
     * @return
     */
    String[] excludes() default {};
}

通过ResponseBodyAdvice对结果拦截

对通过@ResponseBody返回的结果进行拦截(返回的结果即beforeBodyWrite中的Object,此处进行拦截,并进行相关处理)

@ControllerAdvice
public class JsonFilterResponseBodyAdvice implements ResponseBodyAdvice {
    ·····
    @Override
    public Object beforeBodyWrite(Object object, MethodParameter methodParameter, MediaType mediaType, Class converterType, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        JsonFilterObject jsonFilterObject = new JsonFilterObject();
        if (null == object) {
            return null;
        }
        if (!methodParameter.getMethod().isAnnotationPresent(SerializeField.class)) {
            return object;
        }
        /**
         * 处理类进行过滤处理
         */
        if (methodParameter.getMethod().isAnnotationPresent(SerializeField.class)) {
            /**
             * java.lang.reflect.Method.getAnnotation(Class <T> annotationClass)
             * @return: 如果存在于此元素,则返回该元素注释指定的注释类型,否则返回为null
             * 在处理类中,会对应强转为对应的类型,如SerializeField类
             */
            Object obj = methodParameter.getMethod().getAnnotation(SerializeField.class);
            handleAnnotation(SerializeField.class, obj, jsonFilterObject);
        }
        /**
         * 不进行set,返回null,因为未初始化
         */
        jsonFilterObject.setObject(object);
        return jsonFilterObject;
    }

    private void handleAnnotation(Class clazz, Object object, JsonFilterObject jsonFilterObject) {
            /**
             * 获取注解使用处的参数信息,如@SerializeField(clazz = Address.class,includes = {"school", "home", "user"})
             * 获取clazz类型及includes等信息
             */
}

自定义消息转换器

在这里插入图片描述

可以看到,springMVC通过消息转换器,进行最后的输出,通过ResponseBodyAdvice拦截之后,解析到相关信息,初始化了过滤器,在此处,自定义的消息转换器中,调用过滤器,进行过滤相关操作:

public class JsonFilterHttpMessageConverter extends FastJsonHttpMessageConverter {

    private Charset charset;
    private SerializerFeature[] features;

    public JsonFilterHttpMessageConverter() {
        super();
        setSupportedMediaTypes(Arrays.asList(
                new MediaType("application", "json", UTF8),
                new MediaType("application", "*+json", UTF8),
                new MediaType("application", "jsonp", UTF8),
                new MediaType("application", "*+jsonp", UTF8)));
        setCharset(UTF8);
        setFeatures(SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteMapNullValue);
    }

    /**
     *
     * @param obj- the object to write to the output message
     * @param outputMessage- the HTTP output message to write to
     * @throws IOException- in case of I/O errors
     * @throws HttpMessageNotWritableException- in case of conversion errors
     * 官方地址:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/AbstractGenericHttpMessageConverter.html#writeInternal-T-org.springframework.http.HttpOutputMessage-
     */
    @Override
    protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        if(obj instanceof JsonFilterObject){
            JsonFilterObject jsonFilterObject = (JsonFilterObject) obj;
            OutputStream out = outputMessage.getBody();
            SimpleSerializerFilter simpleSerializerFilter = new SimpleSerializerFilter(jsonFilterObject.getIncludes(), jsonFilterObject.getExcludes());
            /**
             * JSON序列化接口toJSONString
             * String toJSONString(Object, SerializeFilter, SerializerFeature...)
             */
            String text = JSON.toJSONString(jsonFilterObject.getObject(), simpleSerializerFilter, features);
            byte[] bytes = text.getBytes(this.charset);
            out.write(bytes);
        }else {
            /**
             * 未声明@SerializeField注解
             */
            OutputStream out = outputMessage.getBody();
            String text = JSON.toJSONString(obj, this.features);
            byte[] bytes = text.getBytes(this.charset);
            out.write(bytes);
        }
    }

    @Override
    public void setCharset(Charset charset) {
        this.charset = charset;
    }

    @Override
    public void setFeatures(SerializerFeature... features) {
        this.features = features;
    }
}

(嵌套类字段的筛选)

    @GetMapping("userMulti")
    @SerializeField(clazz = User.class, includes = {"name", "id", "addresses"})
    /**
     * 进行二次过滤,如使用相同注解SerializeField,Advice处只会判定执行一次
     */
    @MultiSerializeField(clazz = Address.class, excludes = {"user"})
    public User userMulti() {
        User user = new User(1L, "jjc", "123456");
        List<Address> addresses = new ArrayList<>();
        Address a1 = new Address("liuyis's home", "liuyis's school", user);
        Address a2 = new Address("liuyis's home2", "liuyis's school2", user);
        addresses.add(a1);
        addresses.add(a2);
        user.setAddresses(addresses);
        return user;
    }

结果输出:

{
    addresses: [
        {
            home: "liuyis's home",
            school: "liuyis's school"
        },
        {
            home: "liuyis's home2",
            school: "liuyis's school2"
        }
    ],
    id: 1,
    name: "jjc"
}

完整代码可以从github中直接下载运行:https://github.com/MonkeyJJC/JsonFilter

对于复杂bean的过滤,如bean中字段包含容器或其他bean的情况,后续会对轮子进行相关功能完善。

======2018.11.28功能完善:接口响应体格式统一封装

结果返回形式:

{
    code: 200,
    data: {
        id: 2,
        name: "JJC"
    },
    msg: "success"
}

统一处理下接口的返回格式问题,将返回结果封装到“规范模板”中

对于spring消息转换器等概念可以参考:
SpringMVC源码剖析(五)-消息转换器HttpMessageConverter

Spring Boot:定制HTTP消息转换器

Spring MVC返回值处理踩坑笔记

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值