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

这篇文章主要讲 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: ""} ... ]

下载地址:cms-admin-end_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • SpringMVC返回json数据的三种方式

    Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面.Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1,Struts2等. 1.第一种方式是spring2时代的产物,也就是每个json视图controller配置一个Jsoniew. 如:<bean

  • SpringMVC解析JSON请求数据问题解析

    这几年都在搞前后端分离.RESTful风格,我们项目中也在这样用.前几天有人遇到了解析JSON格式的请求数据的问题,然后说了一下解析的方式,今天就写篇文章简单的分析一下后台对于JSON格式请求数据是怎么解析的. 先把例子的代码贴出来: 前端 <input type="button" value="测试JSON数据" onclick="testJSON()" /> <script type="text/javascrip

  • 详解Spring MVC3返回JSON数据中文乱码问题解决

    查了下网上的一些资料,感觉比较复杂,这里,我这几使用两种很简单的办法解决了中文乱码问题. Spring版本:3.2.2.RELEASE Jackson JSON版本:2.1.3 解决思路:Controller的方法中直接通过response向网络流写入String类型的json数据. 使用 Jackson 的 ObjectMapper 将Java对象转换为String类型的JSON数据. 为了避免中文乱码,需要设置字符编码格式,例如:UTF-8.GBK 等. 代码如下: import org.s

  • 详解springmvc 接收json对象的两种方式

    最近学习了springmvc 接收json对象的两种方式,现在整理出来,具体如下: 1.以实体类方式接收 前端 ajax 提交数据: function fAddObj() { var obj = {}; obj['objname'] = "obj"; obj['pid'] = 1 ; $.ajax({ url: 'admin/Obj/addObj.do', method: 'post', contentType: 'application/json', // 这句不加出现415错误:U

  • 详解SpringMVC @RequestBody接收Json对象字符串

    页面提交请求参数有两种,一种是form格式提交,一种json格式提交 通常情况下我们使用的都是form格式提交的数据,数据格式:k=v&k=v,这个时候用springMVC接收参数没有问题,但有时候前端会通过json向后端传递数据,就会出现springMVC获取不到参数值的情况 注意:jQuery的$.post方法虽然也可以传递json格式数据,但实际上是用的form格式提交,jquery会帮你把json转成form格式提交后台 所以其实可以通过$.post,$.get来提交json格式,让jq

  • SpringMVC环境下实现的Ajax异步请求JSON格式数据

    一 环境搭建 首先是常规的spring mvc环境搭建,不用多说,需要注意的是,这里需要引入jackson相关jar包,然后在spring配置文件"springmvc-servlet.xml"中添加json解析相关配置,我这里的完整代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schem

  • Spring mvc 接收json对象

    本文通过代码实例介绍spring mvc 接收json数据的方法,具体详情如下所示: 接收JSON 使用 @RequestBody 注解前台只需要向 Controller 提交一段符合格式的 JSON,Spring 会自动将其拼装成 bean. 1)在上面的项目中使用第一种方式处理返回JSON的基础上,增加如下方法: Java代码 @RequestMapping(value="/add",method=RequestMethod.POST, headers = {"conte

  • Spring MVC 中 AJAX请求并返回JSON的示例

    一.以ModelAndView的方式返回 先看下JavaScript代码: /** * 保存-同步(版本控制库) */ function saveSynchronizedVcHorse(obj) { var ss = $("#SynchronizedSelection div"); var cacheSelectAry = new Array() for(var i = 0; i < ss.length; i ++) { //alert(ss.eq(i).html()); //a

  • Spring mvc实现Restful返回json格式数据实例详解

    在本示例中,我们将向您展示如何将对象转换成json格式并通过spring mvc框架返回给用户. 使用技术及环境: Spring 3.2.2.RELEASE Jackson 1.9.10 JDK 1.6 Eclipse 3.6 Maven 3 PS:在spring 3 中,要输出json数据,只需要添加Jackson 库到你的classpath. 1.项目依赖 spring和jackson的依赖: <project xmlns="http://maven.apache.org/POM/4.

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

    这篇文章主要讲 Spring MVC 如何动态的去返回 Json 数据 在我们做 Web 接口开发的时候, 经常会遇到这种场景. 两个请求,返回同一个对象,但是需要的返回字段并不相同.如以下场景 /** * 返回所有名称以及Id */ @RequestMapping("list") @ResponseBody public List<Article> findAllNameAndId() { return articleService.findAll(); } /** *

  • Spring MVC登录注册以及转换json数据

    项目结构; 代码如下: BookController package com.mstf.controller; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.map.ObjectMapper; import com.mstf.

  • Spring MVC整合Shiro权限控制的方法

    Apache Shiro 是一个功能强大且灵活的开放源代码安全框架,可以细粒度地处理认证 (Authentication),授权 (Authorization),会话 (Session) 管理和加密 (cryptography) 等企业级应用中常见的安全控制流程. Apache Shiro 的首要目标是易于使用和理解. 有时候安全性的流程控制会非常复杂,对开发人员来说是件很头疼的事情,但并不一定如此. 框架就应该尽可能地掩盖复杂性,并公开一个简洁而直观的 API,从而简化开发人员的工作,确保其应

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

    Spring mvc处理json需要使用jackson的类库,因此为支持json格式的输入输出需要先修改pom.xml增加jackson包的引用 <!-- json --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-lgpl</artifactId> <version>1.8.1</version>

  • Spring MVC基于注解的使用之JSON数据处理的方法

    目录 1.JSON数据交互 1.1 JSON概述 1.1.1 对象结构 1.1.2 数组结构 1.2 JSON数据转换 2. HttpMessageConverter 2.1 @RequestBody 2.2 @ResponseBody 1.JSON数据交互 1.1 JSON概述 JSON 是一种轻量级的数据交换格式,是一种理想的数据交互语言,它易于阅读和编写,同时也易于机器解析和生成.JSON有两种数据结构: 对象结构 数组结构 1.1.1 对象结构 对象结构是由花括号括起来的逗号分割的键值对

  • 让你五分钟彻底理解Spring MVC

    目录 概述 MVC 架构模式 传统 MVC 架构模式 Java Web MVC 架构模式 Spring MVC 架构模式 Hello,Spring MVC Spring MVC 依赖引入 DispatcherServlet 声明 Spring 上下文配置 Spring 上下文配置文件内容 HandlerMapping 配置 处理器配置 ViewResolver 配置 DispatcherServlet 组件默认配置 基于注解的 Spring WebMVC DispatcherServlet 请求

  • Spring MVC整合 freemarker及使用方法

    1.什么是Spring MVC? Spring MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将Web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,SpringMVC框架的目的就是帮助我们简化开发. Spring MVC 实现了即用的 MVC 的核心概念.它为控制器和处理程序提供了大量与此模式相关的功能.并且当向 MVC 添加反转控制(Inversion of Control,IoC)时,它使应用程序高度解耦,提供

  • 详解Spring Security 捕获 filter 层面异常返回我们自定义的内容

    通常,我们通过 @ControllerAdvice 和 @ExceptionHandler 来捕获并处理 Controller 层面的异常.但是,filter 是在 controller 层之前的,需要先通过 filter 才能到达 controller 层,此文就介绍一下如何捕获filter层面的异常. Spring 的异常会转发到 BasicErrorController 中进行异常写入,然后才会返回客户端.所以,我们可以在 BasicErrorController 对 filter异常进行

  • Spring MVC Annotation验证的方法

    简介说明 使用Spring MVC的Annotation验证可以直接对view model的简单数据验证,注意,这里是简单的,如果model的数据验证需要有一些比较复杂的业务逻辑性在里头,只是使用annotation做验证是比较难的. 以下是使用Spring MVC自带的annotation验证,加上自定义的一个@Tel的annotation验证例子,此例子具有: 1.支持多语言(国际化) 2.对默认数据先进行转化,比如int.date类型如果传入空值时,会抛异常,默认给定值 先看配置: 1.w

  • Spring MVC返回的json去除根节点名称的方法

    spring xml中配置视图如果是如下 <property name="defaultViews"> <list> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"> </bean> </list> </property> 那么返回结果会是: {"commonAjaxResp

随机推荐