拦截器处理数据并统一返回格式

简介

通过@ResponseResult注解在controller方法上,定义返回的数据是否加上统一的返回数据格式。

内容

例如数据为{name:"张三",age:20},统一返回格式后,数据为{code:0,msg:"请求成功",data:{name:"张三",age:20}},在远程数据交互的时候,具有非常大的作用。

  1. 定义注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    /**
    * 统一结果处理类
    *
    * @author lralin
    * @date 2019年11月26日 17:57:05
    */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Documented
    public @interface ResponseResult {
    }
  2. 定义统一的返回结果类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92

    import java.io.Serializable;

    /**
    * 返回结果
    *
    * @author lralin
    * @create 2019年11月26日 17:03
    **/
    public class SharkResult<T> implements Serializable {
    private int code;
    private String msg;
    private T data;

    public SharkResult() {
    }

    public SharkResult(SharkCode sharkCode, T data) {
    this.code = sharkCode.code();
    this.msg = sharkCode.msg();
    this.data = data;
    }

    private SharkResult(int code, String msg) {
    this.code = code;
    this.msg = msg;
    }

    public void setSharkCode(SharkCode sharkCode) {
    this.code = sharkCode.code();
    this.msg = sharkCode.msg();
    }

    public int getCode() {
    return code;
    }


    public String getMsg() {
    return msg;
    }


    public T getData() {
    return data;
    }

    public void setData(T data) {
    this.data = data;
    }

    //返回成功
    public static SharkResult success() {
    SharkResult sharkResult = new SharkResult();
    sharkResult.setSharkCode(SharkCode.SUCCESS);
    return sharkResult;
    }

    // public static SharkResult success(Object data) {
    // SharkResult sharkResult = new SharkResult();
    // sharkResult.setSharkCode(SharkCode.SUCCESS);
    // sharkResult.setData(data);
    // return sharkResult;
    // }
    //返回成功
    public static <K> SharkResult<K> success(K data) {
    SharkResult<K> sharkResult = new SharkResult<>();
    sharkResult.setSharkCode(SharkCode.SUCCESS);
    sharkResult.setData(data);
    return sharkResult;
    }

    //返回失败
    public static SharkResult failure() {
    SharkResult sharkResult = new SharkResult();
    sharkResult.setSharkCode(SharkCode.FAILURE);
    return sharkResult;
    }

    //返回失败
    public static SharkResult failure(SharkCode sharkCode) {
    SharkResult sharkResult = new SharkResult();
    sharkResult.setSharkCode(sharkCode);
    return sharkResult;
    }

    //返回失败
    public static SharkResult failure(String msg) {
    return new SharkResult(SharkCode.WARN.code(), msg);
    }

    }
  3. 定义统一的结果码类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    * 返回结果 状态
    *
    * @author lralin
    * @create 2019年11月26日 17:03
    **/
    public enum SharkCode {
    SUCCESS(0, "成功"),
    WARN(1001, "其他错误"),
    FAILURE(9999, "失败");

    private int code;
    private String msg;

    SharkCode(int code, String msg) {
    this.code = code;
    this.msg = msg;
    }

    public int code() {
    return this.code;
    }

    public String msg() {
    return this.msg;
    }
    }
  4. 定义拦截器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    import com.xgdfin.common.annotation.ResponseResult;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Method;

    /**
    * 统一包装返回结果
    *
    * @author lralin
    * @create 2019年11月26日 17:26
    **/
    @Component
    @Order(0)
    public class ResponseResultInterceptor implements HandlerInterceptor {
    private static Logger log = LoggerFactory.getLogger(ResponseResultInterceptor.class);

    public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    //请求方法
    if (handler instanceof HandlerMethod) {
    HandlerMethod handlerMethod = (HandlerMethod) handler;
    Class<?> clazz = handlerMethod.getBeanType();
    Method method = handlerMethod.getMethod();
    //判断是否在类上面加了注解
    if (clazz.isAnnotationPresent(ResponseResult.class)) {
    //设置此请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口进行判断
    request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class));
    //判断是否在方法体上面加了注解
    } else if (method.isAnnotationPresent(ResponseResult.class)) {
    //设置此请求返回体,需要包装,往下传递,在ResponseBodyAdvice接口进行判断
    request.setAttribute(RESPONSE_RESULT_ANN, method.getAnnotation(ResponseResult.class));
    }
    }
    return true;
    }
    }
  5. controller类中,方法上添加@ResponseResult注解

    1
    2
    3
    4
    5
    6
    7
    @PostMapping("product")
    @ResponseBody
    @ResponseResult
    public CreditRes insuranceProduct(CreditReq info) {
    CreditRes result = getCreditRes(info);
    return result;
    }
  6. 统一的异常处理和结果处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    /**
    * 异常处理
    *
    * @author lralin
    */
    @ControllerAdvice
    public class ExceptionController implements ResponseBodyAdvice<Object> {
    private final static Logger logger = LoggerFactory.getLogger(ExceptionController.class);
    public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN";

    @ExceptionHandler(BusinessExp.class)
    @ResponseBody
    public SharkResult businessExp(BusinessExp exp) {
    logger.error(exp.getMessage(), exp);
    if (ServletUtils.isAjaxRequest(request)) {
    return SharkResult.failure(exp.getMessage());
    } else {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("errorMessage", exp.getMessage());
    modelAndView.setViewName("error/business");
    return modelAndView;
    }
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public SharkResult ajaxException(Exception e) {
    logger.error("操作异常",e);
    return SharkResult.failure("操作失败,请稍后重试");
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
    ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = requestAttributes.getRequest();
    ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN);
    return responseResultAnn == null ? false : true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
    if (body instanceof SharkResult || !(MediaType.APPLICATION_JSON.equals(mediaType) || MediaType.APPLICATION_JSON_UTF8.equals(mediaType))) {
    return body;
    }
    if (body == null) {
    return SharkResult.failure("请求失败,数据为空");
    }
    return SharkResult.success(body);
    }
    }
  7. 判断是否为ajax请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public static boolean isAjaxRequest(HttpServletRequest request) {
    String accept = request.getHeader("accept");
    if (accept != null && accept.indexOf("application/json") != -1) {
    return true;
    }

    String xRequestedWith = request.getHeader("X-Requested-With");
    if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
    return true;
    }

    String uri = request.getRequestURI();
    if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
    return true;
    }

    String ajax = request.getParameter("__ajax");
    if (StringUtils.inStringIgnoreCase(ajax, "json", "xml")) {
    return true;
    }
    return false;
    }
  8. 前台ajax封装,统一处理返回结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    ;(function ($) {
    $.extend($, {
    customAjax: function (options) {
    var defaults = {
    type: "POST",
    dataType: "json", //返回结果的数据类型
    async: true, //是否异步
    cache: false, //是否缓存
    // timeout: 1000 * 60 * 6,
    data: {},
    //是否处理返回信息
    isHandleSuccess: true,
    success: function (data) {
    }
    };
    var opts = $.extend(defaults, options);

    // if (opts.type.toUpperCase() == "POST") {
    // var params = opts.data;
    // //判断是否为 FormData数据格式。如果是,就把不是文件类型的数据拿出来进行加密传输。
    // if (params instanceof FormData) {
    // var dataTemp = {};
    // // var i = params.entries();
    // // while(i.next()){
    // //
    // // }
    // for (const [key, value] of params.entries()) {
    // if (!(value instanceof File)) {
    // dataTemp[key] = value;
    // }
    // }
    // $.each(dataTemp, function (n, v) {
    // params.delete(n);
    // });
    // var rsaData = $.devilencrypt(JSON.stringify(dataTemp));
    // opts.data.append("securekey", rsaData.encryptedrsa);
    // opts.data.append("securevalue", rsaData.encrypted);
    // } else {
    // var rsaData = $.devilencrypt(JSON.stringify(params));
    // opts.data = {
    // securevalue: rsaData.encrypted,
    // securekey: rsaData.encryptedrsa
    // };
    // }
    // }
    //
    // if (opts.type.toUpperCase() == "GET") {
    // var params = $.urlArgs(opts.url);
    // var rsaData = $.devilencrypt(JSON.stringify(params));
    // var indexT = opts.url.indexOf('?');
    // var newUrl = opts.url.substring(0, indexT);
    // opts.url = newUrl + '?securevalue=' + encodeURIComponent(rsaData.encrypted) + '&securekey=' + encodeURIComponent(rsaData.encryptedrsa);
    // }

    var _beforeSend = opts.beforeSend;
    opts.beforeSend = function (request) {
    if (_beforeSend) {
    _beforeSend(httpRequest, status);
    }
    };

    var _complete = opts.complete;
    opts.complete = function (httpRequest, status) {
    if (_complete) {
    _complete(httpRequest, status);
    }
    };
    //统一处理异常
    var _error = opts.error;
    opts.error = function (event, request, settings) {
    $.alertWarning('网络异常');
    // if (_error) {
    // _error(event, request, settings);
    // }
    };
    var _success = opts.success;
    opts.success = function (data, textStatus, httpRequest) {
    if (httpRequest.getResponseHeader("SessionStatus") == "sessionTimeOut") {
    var loginUrl = httpRequest.getResponseHeader("loginUrl") || '';
    $.confirm('会话失效,是否重新登录', function () {
    // $.alert('You clicked Ok button');
    window.location.href = loginUrl;
    });
    return;
    }
    if (_success) {
    //统一处理错误码
    if (data != null && data.hasOwnProperty("code") && opts.isHandleSuccess) {
    if (data.code != 0) {
    $.alertWarning(data.code == 1001 ? data.msg : "请求失败", 1000);
    return;
    }
    _success(data.data, textStatus, httpRequest);
    } else {
    _success(data, textStatus, httpRequest);
    }
    }
    };
    // ajaxAsync
    $.ajax(opts);
    },
    //处理URL
    urlArgs: function (url) {
    var args = {};
    var index = url.indexOf('?');
    var query = url.substring(index + 1);
    var pairs = query.split('&');
    for (var i = 0; i < pairs.length; i++) {
    var pos = pairs[i].indexOf('=');
    if (pos == -1) {
    continue;
    } // 如果没找到,就跳过
    var name = pairs[i].substr(0, pos); // 获得名称
    var value = pairs[i].substr(pos + 1); // 提取value
    value = decodeURI(value);
    args[name] = value;
    }
    return args
    },
    });
    })(jQuery);

总结

通过统一处理前后台异常数据格式后,在写前端代码的时候,就可以只需考虑成功之后的业务逻辑。而后台的一些校验,可以直接抛出BusinessExp异常。从而大大的增加了写代码的效率和产出。