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

  • 相关阅读:
    『ORACLE』 DBLINK(11g)
    『ORACLE』 对永久表空间进行DDL操作(11g)
    『ORACLE』 对永久表空间进行DML操作(11g)
    『ORACLE』 数据库suspend模式(11g)
    『ORACLE』 数据库quiesce模式(11g)
    『ORACLE』 数据库restricted模式(11g)
    [转]为什么不去读顶级会议上的论文
    learn from 德国老师
    IDA 调试 Android
    L#中 int.TryParse 有问题
  • 原文地址:https://www.cnblogs.com/MattCheng/p/9486370.html
Copyright © 2011-2022 走看看