微服务架构之全局异常(@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
在这里插入图片描述

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异常,但是抛出了一段不友好的信息给客户端
    在这里插入图片描述
  • 所以我们只要对BadSqlGrammarException进行异常信息拦截,客户端只需要提示sql异常即可。
org.springframework.jdbc.BadSqlGrammarException: Error querying database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'cloud_user.r_user' doesn't exist

在这里插入图片描述

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

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、运行结果

  • 返回友好的信息给客户端。
    在这里插入图片描述
    在这里插入图片描述
  • 原因是我们定义的全局异常类对业务层抛出的异常进行了拦截处理,这一段代码处理如下:
  /* 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);
  }
}
  • 测试结果:
    在这里插入图片描述
  • 控制台打印的错误信息如下:
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到了喔!需要的小伙伴,快动手练练吧!

在这里插入图片描述

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
好的,您想了解关于使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理的内容,这是一个常用的方案,可以实现全局异常的捕获和处理,让程序更加健壮和稳定。 @ExceptionHandler注解用于标记一个方法,该方法用于处理指定的异常类型。@ControllerAdvice注解用于标记一个类,该类用于处理全局异常。枚举则可以用于定义异常类型,方便异常处理时的统一管理。 在实现全局异常处理时,我们可以通过 @ExceptionHandler 注解来捕获指定的异常类型,并在方法中进行相应的处理。@ControllerAdvice注解可以让我们在一个类中定义多个 @ExceptionHandler方法,用于处理不同类型的异常。同时,我们也可以使用枚举来定义不同的异常类型,方便管理和调用。 下面是一个简单的示例,演示如何使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理: ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = BusinessException.class) @ResponseBody public ResultVO handleBusinessException(BusinessException e) { return ResultVO.error(e.getCode(), e.getMessage()); } } public enum ExceptionEnum { PARAMETER_ERROR(1001, "参数错误"), DATA_NOT_FOUND(1002, "数据不存在"), SYSTEM_ERROR(5000, "系统错误"); private final int code; private final String message; ExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } public class BusinessException extends RuntimeException { private final int code; public BusinessException(int code, String message) { super(message); this.code = code; } public BusinessException(ExceptionEnum exceptionEnum) { super(exceptionEnum.getMessage()); this.code = exceptionEnum.getCode(); } public int getCode() { return code; } } ``` 在上面的示例中,GlobalExceptionHandler类标记了@ControllerAdvice注解,用于全局异常处理。其中,handleBusinessException方法用于处理BusinessException异常,返回一个ResultVO对象,其中包含错误码和错误信息。 BusinessException则是一个自定义的异常类,它包含一个code属性和一个message属性,用于表示异常的错误码和错误信息。同时,它还提供了一个构造方法,可以根据ExceptionEnum来构造一个BusinessException对象。 ExceptionEnum则是一个枚举类,包含了不同的异常类型,每个异常类型都有一个对应的错误码和错误信息。 在实际开发中,我们可以根据实际需求来定义不同的异常类型和错误码,以便更好地管理和调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值