`

(收藏)Spring MVC 更灵活的控制 json 返回(自定义过滤字段)

 
阅读更多
https://my.oschina.net/u/2328699/blog/836727?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

这篇文章主要讲 Spring MVC 如何动态的去返回 Json 数据 在我们做 Web 接口开发的时候, 经常会遇到这种场景。

两个请求,返回同一个对象,但是需要的返回字段并不相同。如以下场景

/**
* 返回所有名称以及Id
*/
@RequestMapping("list")
@ResponseBody
public List<Article> findAllNameAndId() {
  return articleService.findAll();
}

/**
* 返回所有目录详情
*/
@RequestMapping("list-detail")
@ResponseBody
public List<Article> findAllDetail() {
  return articleService.findAll();
}
Spring MVC 默认使用转json框架是 jackson。 大家也知道, jackson 可以在实体类内加注解,来指定序列化规则,但是那样比较不灵活,不能实现我们目前想要达到的这种情况。
这篇文章主要讲的就是通过自定义注解,来更加灵活,细粒化控制 json 格式的转换。
最终我们需要实现如下的效果:


@RequestMapping(value = "{id}", method = RequestMethod.GET)
// 返回时候不包含 filter 内的 createTime, updateTime 字段
@JSON(type = Article.class, filter="createTime,updateTime") 
public Article get(@PathVariable String id) {
    return articleService.get(id);
}
@RequestMapping(value="list", method = RequestMethod.GET)
// 返回时只包含 include 内的 id, name 字段
@JSON(type = Article.class  , include="id,name")
public List<Article> findAll() {
    return articleService.findAll();
}
jackson 编程式过滤字段

jackson 中, 我们可以在实体类上加上 @JsonFilter 注解,并且通过 ObjectMapper.setFilterProvider 来进行过滤规则的设置。 这里简单介绍一下 setFilterProvider 的使用

@JsonFilter("ID-TITLE")
class Article {
  private String id;
  private String title;
  private String content;
// ... getter/setter
}

// Demo
class Demo {
  public void main(String args[]) {
    ObjectMapper mapper = new ObjectMapper();
    // SimpleBeanPropertyFilter.filterOutAllExcept("id,title")
    // 过滤除了 id,title 以外的所有字段,也就是序列化的时候,只包含 id 和 title
    mapper.setFilterProvider(new SimpleFilterProvider().addFilter("ID-TITLE",
                    SimpleBeanPropertyFilter.filterOutAllExcept("id,title")));

    String filterOut = mapper.writeValueAsString(new Article());

    mapper = new ObjectMapper();
    // SimpleBeanPropertyFilter.serializeAllExcept("id,title")
    // 序列化所有字段,但是排除 id 和 title,也就是除了 id 和 title之外,其他字段都包含进 json
    mapper.setFilterProvider(new SimpleFilterProvider().addFilter("ID-TITLE",
            SimpleBeanPropertyFilter.serializeAllExcept(filter.split("id,title"))));

    String serializeAll = mapper.writeValueAsString(new Article());

    System.out.println("filterOut:" + filterOut);
    System.out.println("serializeAll :" + serializeAll);  
  }
}

输出结果
filterOut:{id: "", title: ""}
serializeAll:{content:""}
封装json转换

通过上面的代码,我们发现,可以使用 setFilterProvider 来灵活的处理需要过滤的字段。不过上面的方法还有一些缺陷就是,还是要在 原来的 model 上加注解,这里我们使用 ObjectMapper.addMixIn(Class<?> type, Class<?> mixinType) 方法,这个方法就是讲两个类的注解混合,让第一个参数的类能够拥有第二个参数类的注解。让需要过滤的 model 和 @JsonFilter 注解解除耦合

package diamond.cms.server.json;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

/**
* depend on jackson
* @author Diamond
*/
public class CustomerJsonSerializer {

    static final String DYNC_INCLUDE = "DYNC_INCLUDE";
    static final String DYNC_FILTER = "DYNC_FILTER";
    ObjectMapper mapper = new ObjectMapper();

    @JsonFilter(DYNC_FILTER)
    interface DynamicFilter {
    }

    @JsonFilter(DYNC_INCLUDE)
    interface DynamicInclude {
    }

    /**
     * @param clazz 需要设置规则的Class
     * @param include 转换时包含哪些字段
     * @param filter 转换时过滤哪些字段
     */
    public void filter(Class<?> clazz, String include, String filter) {
        if (clazz == null) return;
        if (include != null && include.length() > 0) {
            mapper.setFilterProvider(new SimpleFilterProvider().addFilter(DYNC_INCLUDE,
                    SimpleBeanPropertyFilter.filterOutAllExcept(include.split(","))));
            mapper.addMixIn(clazz, DynamicInclude.class);
        } else if (filter !=null && filter.length() > 0) {
            mapper.setFilterProvider(new SimpleFilterProvider().addFilter(DYNC_FILTER,
                    SimpleBeanPropertyFilter.serializeAllExcept(filter.split(","))));
            mapper.addMixIn(clazz, DynamicFilter.class);
        }
    }

    public String toJson(Object object) throws JsonProcessingException {
        return mapper.writeValueAsString(object);
    }
}
我们之前的 Demo 可以变成:

// Demo
class Demo {
  public void main(String args[]) {
    CustomerJsonSerializer cjs= new CustomerJsonSerializer();
    // 设置转换 Article 类时,只包含 id, name
    cjs.filter(Article.class, "id,name", null); 

    String include = cjs.toJson(new Article());

    cjs = new CustomerJsonSerializer();
    // 设置转换 Article 类时,过滤掉 id, name
    cjs.filter(Article.class, null, "id,name"); 

    String filter = cjs.toJson(new Article());

    System.out.println("include: " + include);
    System.out.println("filter: " + filter);  
  }
}
// -----------------------------------
输出结果
include: {id: "", title: ""}
filter: {content:""}
自定义 @JSON 注解

我们需要实现文章开头的那种效果。这里我自定义了一个注解,可以加在方法上,这个注解是用来携带参数给 CustomerJsonSerializer.filter 方法的,就是某个类的某些字段需要过滤或者包含。

package diamond.cms.server.json;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JSON {
    Class<?> type();
    String include() default "";
    String filter() default "";
}
实现 Spring MVC 的 HandlerMethodReturnValueHandler

HandlerMethodReturnValueHandler 接口 Spring MVC 用于处理请求返回值 。 看一下这个接口的定义和描述,接口有两个方法supportsReturnType 用来判断 处理类 是否支持当前请求, handleReturnValue 就是具体返回逻辑的实现。

// Spring MVC 源码
package org.springframework.web.method.support;

import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;

public interface HandlerMethodReturnValueHandler {

    boolean supportsReturnType(MethodParameter returnType);

    void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}
我们平时使用 @ResponseBody 就是交给 RequestResponseBodyMethodProcessor 这个类处理的
还有我们返回 ModelAndView 的时候, 是由 ModelAndViewMethodReturnValueHandler 类处理的
要实现文章开头的效果,我实现了一个 JsonReturnHandler类,当方法有 @JSON 注解的时候,使用该类来处理返回值。

package diamond.cms.server.json.spring;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import diamond.cms.server.json.CustomerJsonSerializer;
import diamond.cms.server.json.JSON;

public class JsonReturnHandler implements HandlerMethodReturnValueHandler{

    @Override
    public boolean supportsReturnType(MethodParameter returnType) { 
        // 如果有我们自定义的 JSON 注解 就用我们这个Handler 来处理
        boolean hasJsonAnno= returnType.getMethodAnnotation(JSON.class) != null;
        return hasJsonAnno;
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
        // 设置这个就是最终的处理类了,处理完不再去找下一个类进行处理
        mavContainer.setRequestHandled(true);

        // 获得注解并执行filter方法 最后返回
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        Annotation[] annos = returnType.getMethodAnnotations();
        CustomerJsonSerializer jsonSerializer = new CustomerJsonSerializer();
        Arrays.asList(annos).forEach(a -> {
            if (a instanceof JSON) {
                JSON json = (JSON) a;
                jsonSerializer.filter(json.type(), json.include(), json.filter());
            }
        });

        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        String json = jsonSerializer.toJson(returnValue);
        response.getWriter().write(json);
    }
}
通过这些,我们就可以最终实现以下效果。


class Article {
  private String id;
  private String title;
  private String content;
  private Long createTime;
// ... getter/setter
}

@Controller
@RequestMapping("article")
class ArticleController {
  @RequestMapping(value = "{id}", method = RequestMethod.GET)
  @JSON(type = Article.class, filter="createTime") 
  public Article get(@PathVariable String id) {
      return articleService.get(id);
  }

  @RequestMapping(value="list", method = RequestMethod.GET)
  @JSON(type = Article.class  , include="id,title")
  public List<Article> findAll() {
      return articleService.findAll();
  }
}
请求 /article/{articleId}

{
    id: "xxxx",
    title: "xxxx",
    content: "xxxx"
}
请求 article/list

[ {id: "xx", title: ""}, {id: "xx", title: ""}, {id: "xx", title: ""} ... ]
分享到:
评论

相关推荐

    spring MVC 对 ResponseBody返回json数据进行脱敏处理

    在Spring MVC框架中,开发Web应用时经常需要将对象转换为JSON格式的数据并返回给客户端。然而,在处理敏感数据时,我们可能希望对这些数据进行脱敏,即隐藏或替换某些字段,以保护用户隐私或者确保数据安全性。本文...

    Spring MVC 更灵活的控制 json 返回问题(自定义过滤字段)

    本文将围绕如何在Spring MVC中灵活控制JSON返回数据,即自定义过滤返回字段的问题,介绍几种不同的实现方法。 首先,我们了解到Spring MVC默认的JSON转换器是Jackson,它支持通过注解在实体类上定义序列化规则。...

    spring mvc拦截器过滤json中的null值

    在Spring MVC框架中,我们经常需要处理JSON数据,这些数据可能包含null值。在某些情况下,为了保持数据的整洁和避免后端处理null值时出现异常,我们需要在数据传输前过滤掉这些null值。本教程将详细介绍如何使用...

    Spring MVC 学习笔记 九 json格式的输入和输出

    Spring MVC 是一个强大的Java Web开发框架,用于构建高效、可维护的Web应用程序。...通过上述讲解,你应该对Spring MVC中JSON的输入和输出有了更深入的理解,能够自如地在实际项目中应用这些技术。

    spring mvc(整合了json)

    将 Spring MVC 与 JSON 整合,可以实现高效、灵活的 Web 服务。 在 Spring MVC 中集成 JSON 的过程主要包括以下几个方面: 1. **JSON 库的引入**:为了处理 JSON 数据,我们通常会引入如 Jackson 或者 Gson 这样的...

    spring mvc生成xml或json

    总结来说,Spring MVC提供了灵活的方式来生成XML和JSON响应。通过`@ResponseBody`注解和适当的转换器,我们可以轻松地将Java对象转化为客户端可读的数据格式。同时,还可以自定义转换逻辑以满足特定需求。了解这些...

    spring mvc json&&jackson jquery js

    Spring MVC还提供了`@JsonView`注解来控制JSON响应中的数据粒度,以及`@JsonProperty`和`@JsonIgnore`来控制哪些字段应包含在JSON中。 **jQuery和JavaScript** jQuery是一个流行的JavaScript库,简化了DOM操作、...

    spring mvc 使用jquery 传json值给Controller时需要解决的问题

    - **类型转换失败**:如果JSON中的某个字段类型与Java对象中的不匹配,Spring MVC可能无法成功转换。确保数据类型一致,或者在Java模型中使用适当的类型转换器。 5. **自定义配置** 如果Spring MVC默认的`...

    springmvc对json支持

    总结起来,Spring MVC对JSON的支持主要依赖于Jackson库,通过`@ResponseBody`和`@RequestBody`注解实现数据交换,配合注解进行类型转换控制,同时允许开发者自定义序列化和反序列化行为,以及配置错误处理机制。...

    spring mvc json学习

    总之,"Spring MVC JSON学习"涵盖了许多关键概念,包括JSON数据的序列化和反序列化、控制器中使用`@RequestBody`和`@ResponseBody`、自定义序列化逻辑以及测试JSON API。掌握这些知识点将使你能够构建出高效、健壮的...

    spring mvc + spring + mybatis+json整合

    以上是关于“spring mvc + spring + mybatis+json整合”的详细说明,这个整合方案是构建Web应用程序的常用技术栈,能够实现高效、灵活且可维护的开发。通过理解并掌握这些知识点,开发者可以更自如地开发和维护复杂...

    SpringMVC 生成JSON(一)

    生成JSON时,我们不再需要ModelAndView,而是直接在Controller方法中返回一个对象或Map,Spring MVC会自动将其转换为JSON格式。这个过程依赖于Jackson库(默认情况下,Spring MVC已经集成了Jackson)。如果项目中未...

    Spring MVC返回JSON数据 用到的jar包

    - **类型安全**:在处理JSON数据时,确保Java对象的属性与JSON字段匹配,避免出现序列化或反序列化错误。 - **定制序列化/反序列化**:通过使用`@JsonAutoDetect`、`@JsonProperty`、`@JsonIgnore`等注解,可以...

    spring3-miniweb.rar_spring json_spring3_spring3 mvc

    `@JsonProperty`注解可以帮助将JSON字段映射到Java对象的属性。 ```java public class User { @JsonProperty("username") private String username; @JsonProperty("email") private String email; // ...

    spring4 mvc json配置jar包

    在开发基于Spring4 MVC的Web应用时,JSON(JavaScript Object Notation)是一种常见的数据交换格式,用于前后端之间的通信。为了使Spring MVC能够处理JSON序列化和反序列化,我们需要引入一系列的Jackson库。这些库...

    基于注解Spring MVC环境搭建

    Spring MVC的核心特性之一,通过注解可以实现字段或构造器级别的依赖注入,避免硬编码依赖关系,提高代码的灵活性和可测试性。 7. **视图解析** Spring MVC使用视图解析器如`InternalResourceViewResolver`来将...

    解决spring mvc JSON 无限死循环

    ### 解决Spring MVC JSON无限死循环问题 在开发基于Spring MVC的应用程序时,经常会遇到将对象转换为JSON格式的需求。然而,在某些情况下,由于对象之间的循环引用,可能会导致JSON序列化过程中出现无限递归的问题...

    spring MVC学习笔记

    通过Spring MVC的配置和使用,我们可以看到其强大的灵活性和控制能力,这些都是通过其大量的配置选项和注解来实现的。无论是参数绑定、返回JSON报文还是增加拦截器,Spring MVC都能提供一种清晰且有效的解决方案。

    spring mvc,spring, hibernate 整合登录实例

    在本文中,我们将深入探讨如何将Spring MVC、Spring和Hibernate这三大流行框架整合在一起,以构建一个完整的登录实例。这个实例旨在提供一个清晰、简洁且易于理解的代码结构,帮助初学者快速上手。 首先,Spring ...

    Spring MVC 的注解使用实例

    Spring MVC通过注解可以实现控制器、方法映射、模型数据绑定、视图解析等关键功能。本实例将深入探讨Spring MVC中常见的注解及其应用。 1. `@Controller` 注解:这个注解用于标记一个类作为Spring MVC的控制器。当...

Global site tag (gtag.js) - Google Analytics