编写一个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<Object>{
/**
* 应用到所有@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<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> 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();
}