微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)

微服务架构之全局异常处理

今天的学习内容是在微服务的公共模块中创建全局异常处理对象,对业务异常进行拦截处理,学完这篇将会学到从零开始也会编写全局异常处理类了。
  • 我们在对业务进行处理时,当数据库操作失败,或遇到未受检测的常异时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚。
  • 但是我们就会在Controller层编写try-catch 捕捉Service 层的异常,然后进行错误信息封装再返回给客户端,否则会返回一些不友好的错误信息到客户端。但是,Controller 层每个方法体都写一些模板化的 try-catch 的代码,这样的代码很冗余、臃肿、难维护,而且要对 Service 层的不同异常进行不同处理,那么业务逻辑就会更复杂。
  • 使用@ControllerAdvice + @ExceptionHandler 进行全局异常处理,可以简化Controller与Service层对业务异常的处理,使代码看起来更简洁、清晰、易维护。
  • @ControllerAdvice 使用在类上,@ExceptionHandler使用在方法上。

一、定义全局异常处理类(GlobalExceptionHandler)

1、在cloud-common模块中创建全局异常处理类GlobalExceptionHandler

cloud-common模块搭建请参考前面章节,链接:https://editor.csdn.net/md/?articleId=109964260
微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)

2、编写全局异常类代码

package yooo.yun.com.common.exception;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import yooo.yun.com.common.api.ApiCode;
import yooo.yun.com.common.api.ApiResult;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * @author wangjiao
 * @since 2020/11/14
 */
@ControllerAdvice
@RestController
@Slf4j
public class GlobalExceptionHandler {

  /**
   * 非法参数验证异常
   *
   * @param ex ex
   * @return res
   */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  @ResponseStatus(value = HttpStatus.OK)
  public ApiResult handleMethodArgumentNotValidExceptionHandler(
      MethodArgumentNotValidException ex) {
    BindingResult bindingResult = ex.getBindingResult();
    List<String> list = new ArrayList<>();
    List<FieldError> fieldErrors = bindingResult.getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
      list.add(fieldError.getDefaultMessage());
    }
    Collections.sort(list);
    log.error("fieldErrors:[ex:{}]", JSON.toJSONString(list));
    return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, list);
  }

  /**
   * 系统登录异常处理
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = SysLoginException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult sysLoginExceptionHandler(SysLoginException exception) {
    log.warn("sysLoginExceptionHandler:系统登录异常[exception:{}]", exception.getMessage());
    return ApiResult.fail(ApiCode.LOGIN_EXCEPTION);
  }

  /**
   * HTTP解析请求参数异常
   *
   * @param e e
   * @return res
   */
  @ExceptionHandler(value = HttpMessageNotReadableException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult httpMessageNotReadableException(HttpMessageNotReadableException e) {
    log.error("httpMessageNotReadableException:[e:{}]", e.getMessage());
    return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, ApiCode.PARAMETER_PARSE_EXCEPTION);
  }

  /**
   * HTTP
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = HttpMediaTypeException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult httpMediaTypeException(HttpMediaTypeException exception) {
    log.error("httpMediaTypeException:[exception:{}]", exception.getMessage());
    return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, ApiCode.HTTP_MEDIA_TYPE_EXCEPTION);
  }

  /**
   * 自定义业务/数据异常处理
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = {SpringBootPlusException.class})
  @ResponseStatus(HttpStatus.OK)
  public ApiResult springBootPlusExceptionHandler(SpringBootPlusException exception) {
    log.error("springBootPlusException:[exception:{}]", exception.getMessage());
    int errorCode;
    if (exception instanceof BusinessException) {
      errorCode = ApiCode.BUSINESS_EXCEPTION.getCode();
    } else if (exception instanceof DaoException) {
      errorCode = ApiCode.DAO_EXCEPTION.getCode();
    } else if (exception instanceof VerificationCodeException) {
      errorCode = ApiCode.VERIFICATION_CODE_EXCEPTION.getCode();
    } else {
      errorCode = ApiCode.SPRING_BOOT_PLUS_EXCEPTION.getCode();
    }
    return new ApiResult().setCode(errorCode).setMsg(exception.getMessage());
  }

  /**
   * 登陆授权异常处理
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = AuthenticationException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult authenticationExceptionHandler(AuthenticationException exception) {
    log.error("authenticationExceptionHandler:[exception:{}]", exception.getMessage());
    return new ApiResult()
        .setCode(ApiCode.AUTHENTICATION_EXCEPTION.getCode())
        .setMsg(exception.getMessage());
  }

  /**
   * 未认证异常处理
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = UnauthenticatedException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult unauthenticatedExceptionHandler(UnauthenticatedException exception) {
    log.error("unauthenticatedExceptionHandler:[exception:{}]", exception.getMessage());
    return ApiResult.fail(ApiCode.UNAUTHENTICATED_EXCEPTION);
  }

  /**
   * 未授权异常处理
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = UnauthorizedException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult unauthorizedExceptionHandler(UnauthorizedException exception) {
    log.error("unauthorizedExceptionHandler:[exception:{}]", exception.getMessage());
    return ApiResult.fail(ApiCode.UNAUTHORIZED_EXCEPTION);
  }

  /**
   * SQL 语法异常
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = BadSqlGrammarException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult badSqlGrammarException(BadSqlGrammarException exception) {
    log.info("badSqlGrammarException:[exception:{}]", exception.getMessage());
    return ApiResult.fail(ApiCode.SQL_ERROR_EXCEPTION);
  }
  /**
   * 默认的异常处理
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = Exception.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult exceptionHandler(Exception exception) {
    log.error("exceptionHandler:[exception:{}]", exception.getMessage());
    if (Objects.nonNull(exception.getMessage())) {
      return ApiResult.fail(exception.getMessage());
    }
    return ApiResult.fail(ApiCode.SYSTEM_EXCEPTION);
  }
}

二、测试未使用全局异常捕捉方法异常

1、启动user模块进行测试

  • 因为common模块是属于微服务的公共模块,所以其他微服务模块只需要在pom文件中引入common模块的依赖就可以直接使用common中配置的信息。
  • user模块引入common模块依赖
 <dependency>
            <groupId>yooo.yun.com</groupId>
            <artifactId>cloud-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
 </dependency>
  • 代码中Controller层使用try catch捕捉service抛出的异常,编码如下:
package yooo.yun.com.user.controller.saas;

import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import yooo.yun.com.common.api.ApiCode;
import yooo.yun.com.common.api.ApiResult;
import yooo.yun.com.common.entity.pojo.UserPoJo;
import yooo.yun.com.common.entity.request.UserLoginReq;
import yooo.yun.com.common.entity.request.UserReq;
import yooo.yun.com.user.service.UserService;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Objects;

/**
 * @author WangJiao
 * @since 2020/10/14
 */
@Slf4j
@RequestMapping(value = "/saas/user")
@RestController("sUserC")
public class UserController {
  @Resource private UserService service;

  /**
   * 用户注册
   *
   * @param req 注册信息
   * @return obj
   */
  @PostMapping("/register")
  @ApiOperation("注册")
  public ApiResult register(@Valid @RequestBody UserReq req){
    log.info("register:[req:{}]", JSON.toJSONString(req));
    if (!Objects.equals(req.getPassword(), req.getRePassword())) {
      return ApiResult.fail(ApiCode.USER_TWO_PASSWORDS_INCONSISTENT);
    }
    try{
      UserPoJo findUser = service.getByTel(req.getTel());
      if (Objects.nonNull(findUser)) {
        return ApiResult.fail(ApiCode.USER_ACCOUNT_REGISTERED);
      }
      // md5加密
      req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
      boolean res = service.save(UserPoJo.of(req));
      log.info("register:[res:{}]", res);
      return ApiResult.ok(res);
    }catch (Exception e) {
      e.printStackTrace();
      return ApiResult.fail(e.getMessage());
    }
  }
}
  • UserService编码:
package yooo.yun.com.user.service;

import yooo.yun.com.common.entity.pojo.UserPoJo;
import yooo.yun.com.common.service.BaseService;

/**
 * @author WangJiao
 * @since 2020/11/12
 */
public interface UserService extends BaseService<UserPoJo> {
  /**
   * query user info by tel
   *
   * @param tel the tel
   * @return user
   */
  UserPoJo getByTel(String tel) throws Exception;
}
  • UserServiceImpl编码:
package yooo.yun.com.user.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import yooo.yun.com.common.entity.pojo.UserPoJo;
import yooo.yun.com.common.service.Impl.BaseServiceImpl;
import yooo.yun.com.user.mapper.UserMapper;
import yooo.yun.com.user.service.UserService;

import javax.annotation.Resource;

/**
 * @author WangJiao
 * @since 2020/11/12
 */
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserPoJo> implements UserService {
  @Resource private UserMapper mapper;
  

  @Override
  public UserPoJo getByTel(String tel) throws Exception{
    log.info("getByTel:[tel:{}]", tel);
    return mapper.selectOne(Wrappers.<UserPoJo>lambdaQuery().eq(UserPoJo::getTel, tel)) throws Exception;
  }
}

2、运行结果

  • sql异常,但是抛出了一段不友好的信息给客户端
    微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)
  • 所以我们只要对BadSqlGrammarException进行异常信息拦截,客户端只需要提示sql异常即可。
org.springframework.jdbc.BadSqlGrammarException: Error querying database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'cloud_user.r_user' doesn't exist

微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)

三、测试使用全局异常捕捉方法异常

1、user测试编码

package yooo.yun.com.user.controller.saas;

import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import yooo.yun.com.common.api.ApiCode;
import yooo.yun.com.common.api.ApiResult;
import yooo.yun.com.common.entity.pojo.UserPoJo;
import yooo.yun.com.common.entity.request.UserLoginReq;
import yooo.yun.com.common.entity.request.UserReq;
import yooo.yun.com.user.service.UserService;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Objects;

/**
 * @author WangJiao
 * @since 2020/10/14
 */
@Slf4j
@RequestMapping(value = "/saas/user")
@RestController("sUserC")
public class UserController {
  @Resource private UserService service;

  /**
   * 用户注册
   *
   * @param req 注册信息
   * @return obj
   */
  @PostMapping("/register")
  @ApiOperation("注册")
  public ApiResult register(@Valid @RequestBody UserReq req){
    log.info("register:[req:{}]", JSON.toJSONString(req));
    if (!Objects.equals(req.getPassword(), req.getRePassword())) {
      return ApiResult.fail(ApiCode.USER_TWO_PASSWORDS_INCONSISTENT);
    }
    UserPoJo findUser = service.getByTel(req.getTel());
    if (Objects.nonNull(findUser)) {
      return ApiResult.fail(ApiCode.USER_ACCOUNT_REGISTERED);
    }

    // md5加密
    req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
    boolean res = service.save(UserPoJo.of(req));
    log.info("register:[res:{}]", res);
    return ApiResult.ok(res);
  }
}
  • UserService代码:
package yooo.yun.com.user.service;

import yooo.yun.com.common.entity.pojo.UserPoJo;
import yooo.yun.com.common.service.BaseService;

/**
 * @author WangJiao
 * @since 2020/11/12
 */
public interface UserService extends BaseService<UserPoJo> {
  /**
   * query user info by tel
   *
   * @param tel the tel
   * @return user
   */
  UserPoJo getByTel(String tel);
}
  • UserServiceImpl实现类编码:
package yooo.yun.com.user.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import yooo.yun.com.common.entity.pojo.UserPoJo;
import yooo.yun.com.common.service.Impl.BaseServiceImpl;
import yooo.yun.com.user.mapper.UserMapper;
import yooo.yun.com.user.service.UserService;

import javax.annotation.Resource;

/**
 * @author WangJiao
 * @since 2020/11/12
 */
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserPoJo> implements UserService {
  @Resource private UserMapper mapper;

  @Override
  public UserPoJo getByTel(String tel) {
    log.info("getByTel:[tel:{}]", tel);
    return mapper.selectOne(Wrappers.<UserPoJo>lambdaQuery().eq(UserPoJo::getTel, tel));
  }
}

2、运行结果

  • 返回友好的信息给客户端。
    微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)
    微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)
  • 原因是我们定义的全局异常类对业务层抛出的异常进行了拦截处理,这一段代码处理如下:
  /* SQL 语法异常
   *
   * @param exception exception
   * @return res
   */
  @ExceptionHandler(value = BadSqlGrammarException.class)
  @ResponseStatus(HttpStatus.OK)
  public ApiResult badSqlGrammarException(BadSqlGrammarException exception) {
    log.info("badSqlGrammarException:[exception:{}]", exception.getMessage());
    return ApiResult.fail(ApiCode.SQL_ERROR_EXCEPTION);
  }

四、Controller层使用@Valid +@RequestBody 校验入参对象属性

  • @Valid +@RequestBody作用于Post或Put接口,校验对象中的基础属性字段,简化校验操作,减少对空参数的校验。
  • user入参对象编码如下:
package yooo.yun.com.common.entity.request;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import java.io.Serializable;

/**
 * 用户表
 *
 * @author WangJiao
 * @since 2019-12-19
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("用户信息请求")
public class UserReq implements Serializable {
  private static final long serialVersionUID = 1L;

  @NotBlank(message = "电话不能为空")
  @ApiModelProperty(value = "电话", example = "15675454322")
  private String tel;

  @NotBlank(message = "密码不能为空")
  @ApiModelProperty(value = "密码", example = "yyy23")
  private String password;

  @NotBlank(message = "确认密码不能为空")
  @ApiModelProperty(value = "确认密码", example = "yyy23")
  private String rePassword;

  @ApiModelProperty(value = "用户头像", example = "http://test.jpg")
  private String avatar;

  @NotBlank(message = "用户名不能为空")
  @ApiModelProperty(value = "姓名", example = "周深")
  private String name;

  public static UserReq of() {
    return new UserReq();
  }
}
  • 测试方法
 /**
   * 用户注册
   *
   * @param req 注册信息
   * @return obj
   */
  @PostMapping("/register")
  @ApiOperation("注册")
  public ApiResult register(@Valid @RequestBody UserReq req){
    log.info("register:[req:{}]", JSON.toJSONString(req));
    if (!Objects.equals(req.getPassword(), req.getRePassword())) {
      return ApiResult.fail(ApiCode.USER_TWO_PASSWORDS_INCONSISTENT);
    }
    UserPoJo findUser = service.getByTel(req.getTel());
    if (Objects.nonNull(findUser)) {
      return ApiResult.fail(ApiCode.USER_ACCOUNT_REGISTERED);
    }

    // md5加密
    req.setPassword(DigestUtils.md5DigestAsHex(req.getPassword().getBytes()));
    boolean res = service.save(UserPoJo.of(req));
    log.info("register:[res:{}]", res);
    return ApiResult.ok(res);
  }
}
  • 测试结果:
    微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)
  • 控制台打印的错误信息如下:
2020-11-24 14:40:36.875 ERROR 21372 [nio-5050-exec-1] y.y.c.c.e.GlobalExceptionHandler         [53] : fieldErrors:[ex:["密码不能为空"]] 
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument at index 0 in method: public yooo.yun.com.common.api.ApiResult yooo.yun.com.user.controller.saas.UserController.register(yooo.yun.com.common.entity.request.UserReq), with 1 error(s): [Field error in object 'userReq' on field 'password': rejected value []; codes [NotBlank.userReq.password,NotBlank.password,NotBlank.java.lang.String,NotBlank];arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userReq.password,password]; arguments []; default message [password]]; default message [密码不能为空]]  
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138) 
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124) 
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) 
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131) 
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) 
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891) 
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) 
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) 
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) 
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) 
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:981) 
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:884) 
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) 
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:858) 
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117) 
        at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) 
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) 
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) 
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) 
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) 
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) 
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) 
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) 
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) 
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) 
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) 
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) 
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
        at java.lang.Thread.run(Thread.java:748) 
	
  • 全局异常对非法参数异常进行了封装,返回友好的提示给前端:
/**
   * 非法参数验证异常
   *
   * @param ex ex
   * @return res
   */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  @ResponseStatus(value = HttpStatus.OK)
  public ApiResult handleMethodArgumentNotValidExceptionHandler(
      MethodArgumentNotValidException ex) {
    BindingResult bindingResult = ex.getBindingResult();
    List<String> list = new ArrayList<>();
    List<FieldError> fieldErrors = bindingResult.getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
      list.add(fieldError.getDefaultMessage());
    }
    Collections.sort(list);
    log.error("fieldErrors:[ex:{}]", JSON.toJSONString(list));
    return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, list);
  }

-_- 到这里,说明你已经get到了喔!需要的小伙伴,快动手练练吧!

微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)

上一篇:springboot + @ControllerAdvice + @ExceptionHandler 实现全局异常拦截,不用


下一篇:统一异常处理@ExceptionHandler