文章目录
前言
在开发中,常常会遇到抛出异常的情况,异常处理除了输出在日志中,还需要提示给用户。我们希望在抛出异常时输出指定的异常信息。当正常操作时按接口要求返回数据,当非正常流程时要获取异常信息进行记录,并提示给用户。
这里只说明使用流程,具体原理较为粗略
一、JSR303简介
JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。
Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。官网
二、规范异常信息
异常处理除了输出在日志中,还需要提示给用户,前端和后端需要作一些约定:
- 错误提示信息统一以json格式返回给前端。
- 以HTTP状态码决定当前是否出错,非200为操作异常。
代码中统一抛出项目的自定义异常类型,这样可以统一去捕获这一类或几类的异常。
规范了异常类型就可以去获取异常信息。
如果捕获了非项目自定义的异常类型统一向用户提示“执行过程异常,请重试”的错误信息。
三、捕获异常
代码统一用try/catch方式去捕获代码比较臃肿,可以通过SpringMVC提供的控制器增强类统一由一个类去完成异常的捕获
如下图:
四、数据校验
1.首先添加需要依赖的包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.常用注解
约束注解名称 | 约束注解说明 |
---|---|
@Null | 用于验证对象为null |
@NotNull | 用于对象不能为null,无法查检长度为0的字符串 |
@NotBlank | 只用于String类型上,不能为null且trim()之后的size>0 |
@NotEmpty | 用于集合类、String类不能为null,且size>0。但是带有空格的字符串校验不出来 |
@Size | 用于对象(Array,Collection,Map,String)长度是否在给定的范围之内 |
@Length | 用于String对象的大小必须在指定的范围内 |
@Pattern | 用于String对象是否符合正则表达式的规则 |
用于String对象是否符合邮箱格式 | |
@Min | 用于Number和String对象是否大等于指定的值 |
@Max | 用于Number和String对象是否小等于指定的值 |
@AssertTrue | 用于Boolean对象是否为true |
@AssertFalse | 用于Boolean对象是否为false |
@Data
public class AddMeetingDto {
@NotEmpty(message = "会议名称不能为空")
@ApiModelProperty(value = "会议名称", required = true)
private String meetingName;
@NotEmpty(message = "优先级不能为空")
@ApiModelProperty(value = "优先级", required = true)
private Integer prio;
@NotEmpty(message = "会议类型不能为空")
@ApiModelProperty(value = "会议类型")
private String meetingType;
@ApiModelProperty(value = "会议描述", required = true)
private String meetingDescription;
@ApiModelProperty(value = "创建人", required = true)
private String createPeople;
@ApiModelProperty(value = "会议室id", required = true)
private Long roomId;
@NotEmpty(message = "参与人员不能为空")
@ApiModelProperty(value = "参与人员", required = true)
List<ParticipantsDto> participants;
@NotEmpty(message = "会议时间不为空")
@ApiModelProperty(value = "会议时间", required = true)
List<FreeTimeDto> freeTime;
}
@ApiOperation("新增会议基础信息")
@PostMapping("/meeting")
public MeetingBaseInfoDto createMeetingBase(@Valid @RequestBody AddMeetingDto addMeetingDto){
Long manageId=12334123L;
MeetingBaseInfoDto meetingBase = meetingInformationService.createMeetingBase(manageId, addMeetingDto);
return meetingBase;
}
五、统一异常处理
从 Spring 3.0 - Spring 3.2 版本之间,对 Spring 架构和 SpringMVC 的Controller 的异常捕获提供了相应的异常处理。
- @ExceptionHandler
Spring3.0提供的标识在方法上或类上的注解,用来表明方法的处理异常类型。 - @ControllerAdvice
Spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强,
在项目中来增强SpringMVC中的Controller。通常和@ExceptionHandler
结合使用,来处理SpringMVC的异常信息。 - @ResponseStatus
Spring3.0提供的标识在方法上或类上的注解,用状态代码和应返回的原因标记方法或异常类。
调用处理程序方法时,状态代码将应用于HTTP响应。
通过上面的两个注解便可实现微服务端全局异常处理,具体代码如下:
首先添加需要依赖的包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
1. 定义一些通用的异常信息
package com.meeting.base.execption;
public enum CommonError {
UNKOWN_ERROR("执行过程异常,请重试。"),
PARAMS_ERROR("非法参数"),
OBJECT_NULL("对象为空"),
QUERY_NULL("查询结果为空"),
REQUEST_NULL("请求参数为空");
private String errMessage;
public String getErrMessage() {
return errMessage;
}
private CommonError( String errMessage) {
this.errMessage = errMessage;
}
}
2. 自定义异常类型
package com.meeting.base.execption;
public class MeetingPlanException extends RuntimeException {
private String errMessage;
public MeetingPlanException() {
super();
}
public MeetingPlanException(String errMessage) {
super(errMessage);
this.errMessage = errMessage;
}
public String getErrMessage() {
return errMessage;
}
public static void cast(CommonError commonError){
throw new MeetingPlanException(commonError.getErrMessage());
}
public static void cast(String errMessage){
throw new MeetingPlanException(errMessage);
}
}
3. 响应用户的统一类型
package com.xuecheng.base.execption;
import java.io.Serializable;
/**
* 错误响应参数包装
*/
public class RestErrorResponse implements Serializable {
private String errMessage;
public RestErrorResponse(String errMessage){
this.errMessage= errMessage;
}
public String getErrMessage() {
return errMessage;
}
public void setErrMessage(String errMessage) {
this.errMessage = errMessage;
}
}
4、全局异常处理器
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(MeetingPlanException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse customException(MeetingPlanException e) {
log.error("【系统异常】{}",e.getErrMessage(),e);
return new RestErrorResponse(e.getErrMessage());
}
@ResponseBody
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public RestErrorResponse exception(Exception e) {
log.error("【系统异常】{}",e.getMessage(),e);
return new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());
}
}
六、分组校验
我们在做校验的时候,通常会遇到一个实体类的添加和修改,他们的校验规则是不同的,所以分组显得尤为重要。他可以帮助我们少建一个冗余的实体类。
用class类型来表示不同的分组,所以我们定义不同的接口类型(空接口)表示不同的分组,如下:
1. 创建分组接口(不需写任何内容)
public class ValidationGroups {
public interface Inster{};
public interface Update{};
public interface Delete{};
}
2. 在定义校验规则时指定分组
@NotEmpty(groups = {ValidationGroups.Inster.class},message = "添加会议名称不能为空")
@NotEmpty(groups = {ValidationGroups.Update.class},message = "修改会议名称不能为空")
// @NotEmpty(message = "会议名称不能为空")
@ApiModelProperty(value = "会议名称", required = true)
private String name;
3. 在Controller方法中启动校验规则指定要使用的分组名:
@ApiOperation("新增会议基础信息")
@PostMapping("/meeting")
public MeetingBaseInfoDto createMeetingBase(@Validated({ValidationGroups.Inster.class}) @RequestBody AddMeetingDto addMeetingDto){
Long manageId=12334123L;
MeetingBaseInfoDto meetingBase = meetingInformationService.createMeetingBase(manageId, addMeetingDto);
return meetingBase;
}
}
总结
以上内容满足基本开发没有什么问题!