8.0 KiB
8.0 KiB
本小节中,我们继续完善项目的基础功能骨架 —— 为认证服务添加全局异常捕获、接口参数校验,定义统一的代码规范,以方便后续更高效率的进行业务开发。
Tip
: 由于这两块的内容在星球第一个项目 中,已经讲解过了,对于相关概念,细节不了解的小伙伴,可翻阅之前的内容,本节内容直接上手实操:
1. 添加全局异常捕获
编辑 xiaohashu-auth 认证服务,如下图所示,分别添加 :
/enums:枚举包, 统一放置相关枚举类;/exception: 异常包,放置异常相关的功能;
考虑到这块的功能和业务本身关联性比较强,所以没有单独提取到
framework基础框架层,直接放在了业务项目内部。
代码如下:
package com.quanxiaoha.xiaohashu.auth.enums;
import com.quanxiaoha.framework.common.exception.BaseExceptionInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ResponseCodeEnum implements BaseExceptionInterface {
// ----------- 通用异常状态码 -----------
SYSTEM_ERROR("AUTH-10000", "出错啦,后台小哥正在努力修复中..."),
PARAM_NOT_VALID("AUTH-10001", "参数错误"),
// ----------- 业务异常状态码 -----------
;
// 异常码
private final String errorCode;
// 错误信息
private final String errorMessage;
}
Tip
: 针对各个微服务,每个服务的异常状态码可以带上服务名(具有唯一性),比如
AUTH-10000, 这样当某个接口报错时,能一样看出来异常是从哪个子服务抛出来的。
package com.quanxiaoha.xiaohashu.auth.exception;
import com.quanxiaoha.framework.common.exception.BizException;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.auth.enums.ResponseCodeEnum;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
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.ResponseBody;
import java.util.Optional;
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获自定义业务异常
* @return
*/
@ExceptionHandler({ BizException.class })
@ResponseBody
public Response<Object> handleBizException(HttpServletRequest request, BizException e) {
log.warn("{} request fail, errorCode: {}, errorMessage: {}", request.getRequestURI(), e.getErrorCode(), e.getErrorMessage());
return Response.fail(e);
}
/**
* 捕获参数校验异常
* @return
*/
@ExceptionHandler({ MethodArgumentNotValidException.class })
@ResponseBody
public Response<Object> handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
// 参数错误异常码
String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode();
// 获取 BindingResult
BindingResult bindingResult = e.getBindingResult();
StringBuilder sb = new StringBuilder();
// 获取校验不通过的字段,并组合错误信息,格式为: email 邮箱格式不正确, 当前值: '123124qq.com';
Optional.ofNullable(bindingResult.getFieldErrors()).ifPresent(errors -> {
errors.forEach(error ->
sb.append(error.getField())
.append(" ")
.append(error.getDefaultMessage())
.append(", 当前值: '")
.append(error.getRejectedValue())
.append("'; ")
);
});
// 错误信息
String errorMessage = sb.toString();
log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage);
return Response.fail(errorCode, errorMessage);
}
/**
* 其他类型异常
* @param request
* @param e
* @return
*/
@ExceptionHandler({ Exception.class })
@ResponseBody
public Response<Object> handleOtherException(HttpServletRequest request, Exception e) {
log.error("{} request error, ", request.getRequestURI(), e);
return Response.fail(ResponseCodeEnum.SYSTEM_ERROR);
}
}
1.1 自测一下
以上代码添加完毕后,编辑 /test2 接口,如下:
手动模拟一个运行时异常 —— 分母不能为 0 ,以测试全局异常捕获功能是否正常:
int i = 1 / 0
重启认证服务,通过 Apipost 调试一波 /test2 接口:
如上图所示,成功捕获到了异常,并返回了通用的异常状态码,以及友好的错误提示信息。
2. 添加接口参数校验
2.1 添加依赖
由于参数校验的依赖,是比较通用的,可以添加到 xiaoha-common 公共模块中:
编辑其 pom.xml , 添加如下依赖:
<!-- 入参校验 -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
2.2 自测一下
依赖添加完毕后,重新刷一下 maven 依赖,然后,我们来自测一下参数校验功能是否好使。编辑 xiaohashu-auth 认证服务中的 User 用户类,为昵称字段添加@NotBlank 校验注解,代码如下:
package com.quanxiaoha.xiaohashu.auth.controller;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User {
/**
* 昵称
*/
@NotBlank(message = "昵称不能为空")
private String nickName;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
接着,为 /test2 接口的入参实体类,添加 @Validated 校验注解,代码如下:
package com.quanxiaoha.xiaohashu.auth.controller;
import com.quanxiaoha.framework.biz.operationlog.aspect.ApiOperationLog;
import com.quanxiaoha.framework.common.response.Response;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@RestController
public class TestController {
// 省略...
@PostMapping("/test2")
@ApiOperationLog(description = "测试接口2")
public Response<User> test2(@RequestBody @Validated User user) {
int i = 1 / 0;
return Response.success(user);
}
}
重启项目,再次自测一波 /test2 接口,将 nickName 昵称字段值设置为空字符串:
可以看到,注解形式的参数校验功能也是没有问题的~