springboot + 全局统一拦截处理异常、统一返回

编写一个Advice继承自ResponseBodyAdvice,

加上注解@ControllerAdvice(basePackages={"xxxx"})

具体的拦截以及返回统一处理,直接上代码——百度+自己编写:

处理了各种异常,以及统一返回的处理实现,排除了下载的流返回处理。

import com.alibaba.fastjson.JSON;
import com.liu.base.ResponseVo;
import com.liu.base.exceptions.CommonException;
import com.liu.constants.enums.ResponseState;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.IbatisException;
import org.apache.shiro.ShiroException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.MethodParameter;
import org.springframework.core.NestedRuntimeException;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;
import java.util.stream.Collectors;

/**
  * 全局异常处理、controller返回格式统一处理
  * @author liuyong
  * @date 2020/12/24
  */
@SuppressWarnings({"unused"})
@Slf4j
@ControllerAdvice(basePackages = {"com.cetccloud"})
public class CustomControllerAdvice implements ResponseBodyAdvice{

    /**
      * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
      * @author liuyong
      * @param binder :
      * @date 2020/12/24 10:21
      */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        //do-nothing
    }


    /**
      * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
      * @author liuyong
      * @param model :
      * @date 2020/12/24 10:21
      */
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("author", "Magical Sam");
    }

    /**
      * 捕捉shiro的异常
      * @author liuyong
      * @param e :
      * @return com.cetccloud.base.ResponseVo
      * @date 2020/12/24 10:21
      */
    @ResponseBody
    @ExceptionHandler(ShiroException.class)
    public ResponseVo handle401(ShiroException e) {
        e.printStackTrace();
        return new ResponseVo.Builder().error(401).message(e.getMessage()).build();
    }

    /**
      * 捕捉CommonException
      * @author liuyong
      * @param e :
      * @return com.cetccloud.base.ResponseVo
      * @date 2020/12/24 10:21
      */
    @ResponseBody
    @ExceptionHandler(CommonException.class)
    public ResponseVo myError(CommonException e) {
        log.error("系统错误CommonException:{}",e.getMessage());
        int code = e.getCode() == 0 ? ResponseState.REQUEST_ERROR.getCode() : e.getCode();
        return new ResponseVo.Builder().error(code).message(e.getMessage()).build();
    }

    /**
      * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常
      * @author liuyong
      * @param e :
      * @return JsonData
      * @date 2020/12/29 上午2:09
      */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseVo methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::
                getDefaultMessage).collect(Collectors.joining(","));
        //下边ResultCodeEnum.PARAMS_BS_ERROR.getCode()就是你自己自定义的返回code码
        return new ResponseVo.Builder().error(ResponseState.PARAM_ERROR.getCode()).message(message).build();
    }

    /**
      * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
      * @author liuyong
      * @param e :
      * @return JsonData
      * @date 2020/12/29 上午2:08
      */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public ResponseVo bindExceptionHandler(BindException e) {
        String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::
                getDefaultMessage).collect(Collectors.joining(","));
        return new ResponseVo.Builder().error(ResponseState.PARAM_ERROR.getCode()).message(message).build();
    }

    /**
      * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是ConstraintViolationException
      * @author liuyong
      * @param e :
      * @return JsonData
      * @date 2020/12/29 上午2:08
      */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResponseVo constraintViolationExceptionHandler(ConstraintViolationException e) {
        String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(","));
        return new ResponseVo.Builder().error(ResponseState.PARAM_ERROR.getCode()).message(message).build();
    }

    /**
      * 捕捉其他所有异常
      * @author liuyong
      * @param request :
      * @param ex :
      * @return com.cetccloud.base.ResponseVo
      * @date 2020/12/24 10:22
      */
    @ResponseBody
    @ExceptionHandler(Throwable.class)
    public ResponseVo globalException(HttpServletRequest request, Throwable ex) {
        //mybatis异常,与数据异常处理
        if(ex instanceof NestedRuntimeException || ex instanceof IbatisException || ex instanceof SQLException){
            log.error("系统错误-数据库错误:", ex);
            return new ResponseVo.Builder().error(ResponseState.SYS_ERROR.getCode())
                    .message("系统错误-数据库错误,请联系管理员!").build();
        }
        ex.printStackTrace();
        return new ResponseVo.Builder().error(getStatus(request).value()).message(ex.getMessage()).build();
    }

    /**
      * 获取Http访问的状态码
      * @author liuyong
      * @param request :
      * @return org.springframework.http.HttpStatus
      * @date 2020/12/24 10:22
      */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

    /*--------------------------------- 以下全局处理,统一返回格式 ------------------------*/

    @Override
    public boolean supports(MethodParameter methodParameter, Class> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        final String returnTypeName = methodParameter.getParameterType().getName();
        //如果无返回值
        if("void".equals(returnTypeName)){
            return new ResponseVo.Builder().ok().build();
        }

        //如果返回值是String类型,单独处理,否则会出现类型转换错误
        if("java.lang.String".equals(returnTypeName)){
            return JSON.toJSONString(new ResponseVo.Builder().ok().data(o).build());
        }

        //如果返回类型是资源类型,不处理
        if(o instanceof Resource){
            return o;
        }
        //如果头信息返回类型不是application/json,不处理
        if(!mediaType.includes(MediaType.APPLICATION_JSON)){
            return o;
        }
        //如果用户自己返回已经定义为统一返回的格式,不处理
        if(returnTypeName.endsWith(".ResponseVo")){
            return o;
        }

        //其他情况,统一处理为统一的格式
        return new ResponseVo.Builder().ok().data(o).build();
    } 
  

 

你可能感兴趣的:(springboot,spring,boot,java,restful)