在进行练习之前,建议先配置好Swagger,测试更方便。
一、参数校验
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.常用注解
- @NotNull 限制传入的值不为null。作用在int,Integer等数值类型上时,在限制不为null的同时也不能为空字符串。作用在String上时,限制不为null但是可以为空字符串。作用在类上时,限制不为null。
- @NotEmpty 作用于String类型,限制不能为null,且长度不为0,可以是空格字符串。
- @NotBlank 作用于String类型,限制不能为null,且去掉空格,即trim之后,长度不为0。
- @Max 作用于数值类型,限制最大值(包含该最大值)
- @Min 作用于数值类型,限制最小值(包含该最小值)
- @Email 作用于String类型,验证是否符合email格式
3.DTO层代码使用以上注解进行限制
@Data
public class AddUsersDTO implements Serializable {
@NotBlank(message = "用户名不能为空")
private String userName;
@NotBlank(message = "密码不能为空")
private String passWord;
@NotBlank(message = "性别不能为空")
private String user_sex;
@NotBlank(message = "昵称不能为空")
private String nick_name;
@NotNull(message = "年龄不能为空")
@Min(value = 0, message = "年龄不能小于 0")
@Max(value = 200, message = "年龄不能大于 200")
private Integer age;
}
3.Controller层代码使用@Validated注解进行参数校验
@RestController
@Slf4j
@RequestMapping("/users")
@Api(tags = {"用户管理"})
public class UsersController {
@Autowired
private UsersService usersService;
@PostMapping("/add")
@ApiOperation("添加用户")
public ResponseUtil addUesrs(
@RequestBody @Validated AddUsersDTO addUsersDTO) {
return usersService.addUsers(addUsersDTO);
}
}
4.通过Swagger或者postman进行验证
测试数据:将昵称设置为“ ”空格字符串
{
"age": 0,
"nick_name": " ",
"passWord": "string",
"userName": "string",
"user_sex": "string"
}
返回的数据:
{
"timestamp": "2020-05-21T11:39:07.655+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotBlank.addUsersDTO.nick_name",
"NotBlank.nick_name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"addUsersDTO.nick_name",
"nick_name"
],
"arguments": null,
"defaultMessage": "nick_name",
"code": "nick_name"
}
],
"defaultMessage": "昵称不能为空",
"objectName": "addUsersDTO",
"field": "nick_name",
"rejectedValue": " ",
"bindingFailure": false,
"code": "NotBlank"
}
],
"message": "Validation failed for object='addUsersDTO'. Error count: 1",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.xl.exceptiondemo.utils.ResponseUtil com.xl.exceptiondemo.controller.UsersController.addUesrs(com.xl.exceptiondemo.model.AddUsersDTO): [Field error in object 'addUsersDTO' on field 'nick_name': rejected value [ ]; codes [NotBlank.addUsersDTO.nick_name,NotBlank.nick_name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [addUsersDTO.nick_name,nick_name]; arguments []; default message [nick_name]]; default message [昵称不能为空]] \n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:139)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1579)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\n",
"path": "/users/add"
}
我们看到,这样我们做前后端分离就很不友好,其实我们只需要其中的“昵称不能为空"这条信息就够了,所以我们要进行统一异常处理,返回我们想要的结果。
二、统一异常处理
1.构造响应实体
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseUtil<T> implements Serializable {
// 响应数据
private T data;
// 响应状态码
private String code;
// 响应信息
private String msg;
public static<T> ResponseUtil<T> succeed(T data, String code, String msg) {
return new ResponseUtil(data, code, msg);
}
public static<T> ResponseUtil<T> failed(T data, String code, String msg) {
return new ResponseUtil(data, code, msg);
}
}
2.自定义响应状态码以及响应信息
public interface CommonContants {
/**
* 成功响应的 code 以及 msg
*/
String SUCCESS = "200";
String SUCCESS_MSG = "SUCCESS";
/**
* 失败响应的 code 以及 msg
*/
String ARGS_ERROR = "E000001";
String ARGS_ERROR_MSG = "参数错误";
String SERVER_ERROR = "E000002";
String SERVER_ERROR_MSG = "服务器异常";
String ERROR = "E999999";
String ERROR_MSG = "未知异常";
}
3.编写全局统一异常处理类,使用@ControllerAdvice注解
通过@Validated注解进行参数校验,如果不符合规则,就会抛出MethodArgumentNotValidException异常,所以我们要对MethodArgumentNotValidException异常进行处理。
@ResponseBody // 记得添加此注解,以json格式返回
@ControllerAdvice
public class MyExceptionAdvice {
/**
* 参数校验异常统一处理
* 返回状态码:200
*/
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseUtil handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return ResponseUtil.failed(null, CommonContants.ARGS_ERROR,
e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}
/**
* 所有异常统一处理
* 返回状态码:500
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ResponseUtil handleException(Exception e) {
return ResponseUtil.failed(null, CommonContants.SERVER_ERROR,
CommonContants.SERVER_ERROR_MSG);
}
}
4.验证
测试数据:将昵称设置为“ ”空格字符串
{
"age": 0,
"nick_name": " ",
"passWord": "string",
"userName": "string",
"user_sex": "string"
}
返回数据:
{
"data": null,
"code": "E000001",
"msg": "昵称不能为空"
}
这样,通过和统一异常处理的结合,可以友好的实现前后端分离。