用一个切面来统一返回前端的JSON格式

新项目使用Spring MVC + MyBatis架构来做,这套框架自己应该比较得心应手,这里来写一下这两天做的一些设计。

首先是静态资源的处理,关于这个之前有写文章单独讲过,这里不再重复写,不太清楚的童鞋可以移步查看

接着需要统一JSON的返回格式,和前端工程师约定,对于字符串类型和日期类型都返回字符串,而对于普通数字类型的话都返回数字,金额类数字都返回格式化好的保留一位小数的字符串(比如”10.0”),另外数字和金额的默认值为0,而字符串的默认值为字符串空(“”),并且对于返回的格式也有规范,所有的JSON返回必须形如:

{
    "status": 1,
    "data": data,
    "msg": ""
}

其中:

  • status代表了该请求的成功与否,若为1表示成功,若为0表示失败,此时msg中将包含显示给用户的错误信息
  • data代表了返回给前端的数据,可以是一个复杂的数据结构
  • msg前面提到了,一般放异常信息。

下面,我们首先需要对默认的Jackson的序列化规则做出修改,这里自定义一个ObjectMapper

/**
 * 自定义jackson解析器
 * @author Zhu
 * @date 2015-5-14
 * @version 0.0.1
 * @description
 */
@Component
public class CustomerObjectMapper extends ObjectMapper {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    /**
     *
     */
    public CustomerObjectMapper() {

        super();
//      this.setSerializationInclusion(Include.NON_NULL);
        // 允许单引号
//        this.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        // 字段和值都加引号
//        this.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // 数字也加引号
//        this.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
//        this.configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, true);
        this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>(){
            @Override
            public void serialize(Object value, JsonGenerator generator,
                    SerializerProvider provider) throws IOException,
                    JsonProcessingException {
                generator.writeString("");
            }
        });
    }

}

然后将我们自定义的ObjectMapper注入到MappingJackson2HttpMessageConverter中去并重新配置Spring MVC中的message converter

    <bean id="mappingJackson2HttpMessageConverter"
        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper" ref="customerObjectMapper"></property>
    </bean>

    <mvc:annotation-driven>
        <mvc:message-converters>
            <ref bean="mappingJackson2HttpMessageConverter"/>
        </mvc:message-converters>
    </mvc:annotation-driven>

接下来,我们可以在Controller方法上添加@ResponseBody注解来返回JSON(这里需要说一句,classpath下需要有jackson的jar包,这是前提),但是这就要求我们在每个Controller方法中都进行一些重复的工作,比如:

    public ModelMap getUserList(){
        ModelMap modelMap = new ModelMap();
        try {
            modelMap.put("status", 1);
            modelMap.put("data", userSerivce.getUserList());
            modelMap.put("", "");
        } catch (Exception e) {
            modelMap.put("status", 0);
            modelMap.put("data", "");
            modelMap.put("msg", e.getMessage());
            logger.error("getUserList occurs error:", e);
        }
    }

这样的重复代码在项目中不建议出现,我们其实只需要关心其中的data就可以了,所以我打算将其统一写到一个方法中,并且这也有利于以后对于公共结构的更改。下面就是一个切面来统一处理所有的接口:

/**
 * 其中ResponseBase类和注解类CustomResponseBody都是自己写的
 */

/**
 * @author Zhu
 * @date 2015-5-18
 * @version 0.0.1
 * @description 负责将返回转换成统一消息格式
 * 序列化为的格式如下:
 * {
    "status": 1,
    "data": data,
    "msg": ""
    }
    其中被注解的方法只需要关心data的内容即可
 */
@Aspect
@Component
public class ResponseAspect {

    @Resource
    private MappingJackson2HttpMessageConverter converter;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 拦截所有@ResponseBody
     * @author Zhu
     * @date 2015-5-18上午11:32:26
     * @description
     */
    @Pointcut("execution(* com.xxx.*.web.controller.*.*(..)) && @annotation(com.xxx.common.annotation.CustomResponseBody)")
    public void responseBodyPointCut() {

    }

    /**
     * @author Zhu
     * @date 2015-5-18上午11:35:42
     * @description
     * @param pjp
     * @throws Throwable
     */
    @Around(value = "responseBodyPointCut()")
    @ResponseBody
    public void formatResult2JSON(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = pjp.proceed();
        ResponseBase responseBase = new ResponseBase();
        responseBase.setData(ret);

        HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
        HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
        converter.write(responseBase, MediaType.APPLICATION_JSON, outputMessage);
        shutdownResponse(response);
    }

    /**
     *
     * @author Zhu
     * @date 2015-5-18下午6:01:46
     * @description
     * @param jp
     * @param error
     * @throws Throwable
     */
    @AfterThrowing(pointcut = "responseBodyPointCut()", throwing = "error")
    public void handleForException(JoinPoint jp, Throwable error) throws Throwable{
        ResponseBase responseBase = new ResponseBase();
        responseBase.setStatus(0);
        responseBase.setMsg(error.getMessage());
        HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
        logger.error(jp.getSignature().getName() + "-error!", error);
        HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
        converter.write(responseBase, MediaType.APPLICATION_JSON, outputMessage);
        shutdownResponse(response);
    }

    private void shutdownResponse(HttpServletResponse response) throws IOException{
        response.getOutputStream().close();
    }
}


当然这里有几个关键点,需要提一下:

  1. 这也就提到了一个如何在Spring中手动使用配置好的Jackson来序列化输出,这里我注入MappingJackson2HttpMessageConverter,因为这里想要统一项目的JSON序列化都交由Jackson来做
  2. 由于是对Controller的代理,而一般Controller是不实现接口的,那么就无法使用JDK自带的动态代理,需要用到cglib来做
  3. 关于上下文的问题,这里我在做的时候,由于碰到了两个上下文,即一般所说的rootContext和webContext。。。。
  4. 在自己返回json的情况下,需要shutdownResponse,即将Response关掉,不然可能会在这里输出json后还会再次输出一些内容

其余的内容后续再写吧,现在手头上项目太多,需要整理的东西也太多了~

时间: 2024-12-17 12:06:23

用一个切面来统一返回前端的JSON格式的相关文章

后端返回值以json的格式返回,前端以json格式接收

以随便一个类为例子:这个例子是查询企业主营类别前5事项 (1)后端将结果绑定到param中,然后将结果以为json的格式返回到前端 /** * 查询企业主营类别前5事项 * @param request * @param response * @param config * @throws Exception * @author hongxy * 2017年6月1日下午2:21:14 */ public void getEnterpriseMainCategory(HttpServletRequ

Asp.Net WebAPI配置接口返回数据类型为Json格式

一.默认情况下WebApi 对于没有指定请求数据类型类型的请求,返回数据类型为Xml格式 例如:从浏览器直接输入地址,或者默认的XMLRequest,或者AngularJs的get请求等. 对于有循环引用的也会抛出异常""ObjectContent`1"类型未能序列化内容类型"application/xml; charset=utf-8"的响应正文." 二.设置返回格式为Json数据 1.修改配置,这对所有的接口都生效 找到Global.asax

【.net 深呼吸】聊聊WCF服务返回XML或JSON格式数据

有时候,为了让数据可以“跨国经营”,尤其是HTTP Web有关的东东,会将数据内容以 XML 或 JSON 的格式返回,这样一来,不管客户端平台是四大文明古国,还是处于蒙昧时代的原始部落,都可以使用这些数据. 在WCF中实现将数据以XML或JSON格式返回有Y多种方法,不管你用什么方法,只要得到预期结果就好,米芾说了,笔可以八面出锋,当然了,人家指的是绘画. 这里,老周就挑两种方法来演示,仅供参考,没有考古价值,建议司马子长不要把本文收入<史记>. 第一种方法是用到 WebServiceHos

SpringBoot RestController 同时支持返回xml和json格式数据

@RestController 默认支持返回json格式数据,即使不做任何配置也能返回json数据 当接口需要支持xml或json两种格式数据时应该怎么做呢? 只要引入 Jackson xml的 maven依赖就可以了: <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-xml-provider</artifactId> &l

django返回页面和json格式列表给前端AntV图表使用。

ret_char_data = [] # 经费支出分类汇总列表 ret_char_data_root = [] # 经费支出分类汇总列表含总经费 budget_obj = models.Budget.objects.filter(id=request.GET.get("id")).first() payment_year = time.strptime(budget_obj.year_budget, "%Y")[0] payment_previous_year =

WCF服务返回XML或JSON格式数据

第一种方式public string GetData( string format) { string res = null; Student stu = new Student { StuID = 3, StuName ="李四" }; using (MemoryStream ms = new MemoryStream()) { XmlObjectSerializer sz = null; if ( format.ToLower() == "xml") { sz

ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间.我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作效率. 一.准备工作 定义响应实体类 /// <summary> /// 响应实体类 /// </summary> public class ResultModel { /// <summary> /// 状态码 /// </summary> public in

一个粗心的Bug,JSON格式不规范导致AJAX错误

一.事件回放  今天工作时碰到了一个奇怪的问题,这个问题很早很早以前也碰到过,不过没想到过这么久了竟然又栽在这里. 当时正在联调一个项目,由于后端没有提供数据接口,于是我直接本地建立了一个 json 文件,然后把配置的URL指向这个json文件,文件内容大概如下 : // account.json { success: true, data: [{ id: "1", name: "张XX", job: "员工", type: 1 }] } 嗯,一

ajax使用json数组------前端往后台发送json数组及后台往前端发送json数组

1.引子 Json是跨语言数据交流的中间语言,它以键/值对的方式表示数据,这种简单明了的数据类型能被大部分编程语言理解.它也因此是前后端数据交流的主要方式和基础. 2.前端往后台传输json数据 第一步,先应该定义一个JSON对象或JSON数组.json对象是“var jsonObj={“name1”:“value1” , “name2”:“value2” , “name3”:“value3”,…};”的格式定义,而json数组则是以中括号"[ ]"包裹多个json对象的格式定义,如