zoukankan      html  css  js  c++  java
  • Spring MVC 使用介绍(十二)控制器返回结果统一处理

    一、概述

    在为前端提供http接口时,通常返回的数据需要统一的json格式,如包含错误码和错误信息等字段。

    该功能的实现有四种可能的方式:

      • AOP 利用环绕通知,对包含@RequestMapping注解的方法统一处理
        • 优点:配置简单、可捕获功能方法内部的异常
        • 缺点:aop不能修改返回结果的类型,因此功能方法的返回值须统一为Object类型
      • filter 在过滤器层统一处理
        • 优点:配置简单
        • 缺点:无法识别异常结果,须对返回结果进行额外的反序列化
      • 拦截器  获取返回值不方便,且无法获取到String类型的返回值,无法实现该功能
      • HandlerMethodReturnValueHandler 无上述各方法的缺点,且能复用@ResponseBody等注解,为该功能的完美实现方案

    二、基于HandlerMethodReturnValueHandler的实现方案

    HandlerMethodReturnValueHandler是spring mvc为统一处理控制器功能方法返回结果的接口类,为策略模式实现,视图名称的解析和@ResponseBody输出json等功能均为基于该接口的实现。spring mvc处理流程的源码分析可参考自定义统一api返回json格式

    具体实现方案如下:

    定义统一的返回实体

    public class ResponseInfo {
        public static final int ERROR_CODE_SUCCESS = 0;
        public static final int ERROR_CODE_MAPPING_FAILED = 100;
        public static final int ERROR_CODE_BUSINESS_FAILED = 130;
    
        /**
         * 错误码
         */
        private int errorCode;
    
        /**
         * 错误信息
         */
        private String errorMsg;
    
        /**
         * 数据
         */
        private Object data;
    ... }

    自定义HandlerMethodReturnValueHandler实现类

    /**
     * 对controller返回的数据统一封装为ResponseInfo,注意:
     * 1、controller异常由spring mvc异常机制处理,会跳过该处理器
     * 2、该处理器仅处理包含@RestController、@ResponseBody注解的控制器*/
    public class MyHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) {
    Class
    <?> controllerClass = returnType.getContainingClass(); returnType.getMethodAnnotation(ResponseBody.class); return controllerClass.isAnnotationPresent(RestController.class) || controllerClass.isAnnotationPresent(ResponseBody.class) || returnType.getMethodAnnotation(ResponseBody.class) != null; } @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest)
    throws Exception { ResponseInfo responseInfo = new ResponseInfo(); if (returnValue instanceof ResponseInfo) { responseInfo = (ResponseInfo) returnValue; } else { responseInfo.setData(returnValue); } // 标识请求是否已经在该方法内完成处理 mavContainer.setRequestHandled(true); HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); response.setContentType("application/json;charset=UTF-8"); response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); response.getWriter().write(JSON.toJSONString(responseInfo)); } }

    实例化和注册该处理器

    @Configuration
    public class WebConfig implements ApplicationContextAware {
        
        /**
         * 实例化为bean
         */
        @Bean
        public MyHandlerMethodReturnValueHandler myHandlerMethodReturnValueHandler() {
            return new MyHandlerMethodReturnValueHandler();
        }
    
        /* 
         * 注册到容器,采用这种注册方式的目的:
         * 自定义的HandlerMethodReturnValueHandler放在默认实现的前面,从而优先采用自定义处理策略
         * 否则,无法覆盖@ResponseBody处理机制,且String类型的返回值将默认由ViewNameMethodReturnValueHandler处理而映射为视图名
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            
            RequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
            List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
            handlers.add(this.myHandlerMethodReturnValueHandler());
            handlers.addAll(handlerAdapter.getReturnValueHandlers());
            handlerAdapter.setReturnValueHandlers(handlers);
        }
    }

    spring-context.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
               http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
               http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    
        <context:property-placeholder location="classpath:application.properties"/>
    
        <context:component-scan base-package="cn.matt" use-default-filters="true">
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
          <context:exclude-filter type="assignable" expression="cn.matt.common.web.WebConfig" />
        </context:component-scan>
    
    </beans>

    spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:mvc="http://www.springframework.org/schema/mvc" 
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
               http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
               http://www.springframework.org/schema/mvc 
               http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
               http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context-4.2.xsd">
              
        <mvc:annotation-driven />
    
        <context:component-scan base-package="cn.matt" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController" />
            <context:include-filter type="assignable" expression="cn.matt.common.web.WebConfig" />
        </context:component-scan>
    
    </beans>

    控制器基类

    public class BaseController {
        
        @ExceptionHandler  
        public void exp(HttpServletRequest request, HttpServletResponse response, Exception ex) throws IOException {  
            response.setContentType("text/plain;charset=UTF-8");
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0);
            
            ResponseInfo responseInfo = new ResponseInfo();
            responseInfo.setErrorCode(ResponseInfo.ERROR_CODE_MAPPING_FAILED);
            responseInfo.setErrorMsg(ex.getMessage());
            response.getWriter().write(JSON.toJSONString(responseInfo));
        }  
    }

    测试控制器

    @RestController
    @RequestMapping("/test")
    public class TestController extends BaseController {
    
        @RequestMapping(value = "/hello")
        public String hello() {
            return "hello";
        }
        
        @RequestMapping(value = "/user")
        public UserInfo getUser() {
            UserInfo userInfo = new UserInfo();
            userInfo.setUserName("matt");
            userInfo.setProvince("安徽");
            userInfo.setCity("阜阳");
            return userInfo;
        }
    }

    启动后,输入http://localhost:8080/wfc-web/test/user,http输出:

    {"data":{"city":"阜阳","province":"安徽","userName":"matt"},"errorCode":0}

    * 上述基于继承的异常处理方式,有一定侵入性,基于@RestControllerAdvice 或 @ControllerAdvice注解的方案无侵入性,详细可参考 Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理

    参考:

    SpringMVC HandlerMethodReturnValueHandler解读

    spring mvc 处理Controller返回结果和HandlerMethodReturnValueHandler使用

    自定义统一api返回json格式(app后台框架搭建三)

    springmvc获取上下文ApplicationContext

  • 相关阅读:
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    OA办公系统 Springboot Activiti6 工作流 集成代码生成器 vue.js 前后分离 跨域
    java企业官网源码 自适应响应式 freemarker 静态引擎 SSM 框架
    java OA办公系统源码 Springboot Activiti工作流 vue.js 前后分离 集成代码生成器
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    java 视频播放 弹幕技术 视频弹幕 视频截图 springmvc mybatis SSM
    最后阶段总结
    第二阶段学习总结
    第一阶段学习总结
  • 原文地址:https://www.cnblogs.com/MattCheng/p/9486370.html
Copyright © 2011-2022 走看看