前言:
本文概述如何实现,如果有技术点不懂之处,自行查阅资料证明。SpringParent版本2.3.4.RELEASE
准备工作:
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--请求参数快捷校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- resource目录下创建i18n文件夹,再创建messages_zh_CN.properties文件,做为异常信息配置文件,因为不需要识别国际化语言,所以直接使用中文文件,如图:
- 创建配置类,把国际化文件操作对象设置参数注入到容器
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
@Configuration
public class MessageConfig {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
/*指定文件路径及名称:
messages.properties(默认文件,非中、英文时读取)
messages_en.properties(英文)
messages_zh.properties(中文)*/
messageSource.setBasename("classpath:i18n/messages");
/*设置字符集,IDEA需要在 File | Settings | Editor | File Encodings中设置字符集为 UTF-8*/
messageSource.setDefaultEncoding("UTF-8");
/*每小时刷新一次缓存*/
messageSource.setCacheSeconds(3600);
return messageSource;
}
}
- 构建MessageSource操作工具类
@Component
public class MessageSourceUtils {
/**
* 指定的消息不存在时返回的消息。
*/
private static final String MSG_NOT_EXIST = "消息不存在";
@Resource
private MessageSource messageSource;
private MessageSourceAccessor accessor;
@PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource, Locale.CHINA);
}
/*根据code获取信息*/
public String get(String code) {
return accessor.getMessage(code);
}
/*根据code获取动态信息*/
public String getMsg(String code,String... args) {
String text = null;
/*根据CODE获取消息*/
// String message = messageSource.getMessage(code, args, Locale.CHINA);
/*取得默认消息内容*/
try {
text = get(code);
} catch (NoSuchMessageException e) {
// TODO 日志打印
System.out.println("错误");
}
/*参数动静结合,获取最终的参数结果*/
text = text == null ? MessageFormat.format(MSG_NOT_EXIST, null) : MessageFormat.format(text, args);
return text;
}
}
- 创建自定义全局异常类
@Getter
public class ManageException extends RuntimeException{
/*
* 错误代码
*/
private String failCode;
/*
* 错误信息
*/
private String[] failMsg;
public ManageException(String code, String... msg){
this.failCode = code;
this.failMsg = msg;
}
}
- 创建自定义拦截器类,@RestControllerAdvice+@ExceptionHandler(要拦截的异常类)组合实现全局异常拦截,不懂注解的自行百度
import com.infinity.honghu.exception.model.ManageException;
import com.infinity.honghu.model.response.RespResult;
import com.infinity.honghu.utils.MessageSourceUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalControllerAdvice {
@Resource
private MessageSourceUtils msgUtils;
/**
* @Author HongHu
* @Description manage模块的异常
*/
@ExceptionHandler(ManageException.class)
public RespResult manageExceptionHandler(ManageException m){
String msgCode = m.getFailCode();
String msgInfo = msgUtils.getMsg(msgCode,m.getFailMsg());
return RespResult.error(msgCode,msgInfo);
}
/**
* @Author HongHu
* @Description 处理绑定异常,bean中有字段验证,Validated Valid 注解指定要验证这个bean对象,当前端传过来一个表单格式
* (Content-Type: multipart/form-data)的数据,后台通过需要验证的bean对象接收的时候,加入验证不通过, 则会报此异常
* 处理form data方式调用接口校验失败抛出的异常
*/
@ExceptionHandler(BindException.class)
public RespResult bindExceptionHandler(BindException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<String> collect = fieldErrors.stream()
.map(o -> o.getDefaultMessage())
.collect(Collectors.toList());
return new RespResult().success(collect);
}
/**
* @Author HongHu
* @Description 请求体绑定异常,与BindException类似,不同的是因为什么触发,当Controller接收的是一个json格式,@RequestBody接收参数时,
* 验证失败会抛出此异常,处理 json 请求体调用接口校验失败抛出的异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public RespResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
List<String> collect = fieldErrors.stream()
.map(o -> o.getDefaultMessage())
.collect(Collectors.toList());
return new RespResult().success(collect);
}
/**
* @Author HongHu
* @Description Controller中的参数校验失败会抛出此类异常,类头部需要添加@Valited注解,处理单个参数校验失败抛出的异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public RespResult constraintViolationExceptionHandler(ConstraintViolationException e) {
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
List<String> collect = constraintViolations.stream()
.map(o -> o.getMessage())
.collect(Collectors.toList());
return new RespResult().success( collect);
}
/**
* @Author HongHu
* @Description 处理程序员在程序中未能捕获(遗漏的)异常
*/
@ExceptionHandler(Exception.class)
public RespResult exception(Exception e) {
return RespResult.error("99999",e.getStackTrace().toString());
}
- 创建请求模型
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
@Data
public class ReqParam {
@NotBlank(message = "name不能为空")
private String name;
@NotBlank(message = "age不能为空")
@Min(value=5,message = "age不能小于5")
private String age;
}
- 响应模型
@Data
public class RespResult<T> implements Serializable {
private String code;
private String msg;
private T data;
/* 成功且返回体有数据 */
public static RespResult success(Object data) {
RespResult retSuc = new RespResult();
retSuc.setCode(RespEnum.SUCCESS.getCode());
retSuc.setMsg(RespEnum.SUCCESS.getMsg());
retSuc.setData(data);
return retSuc;
}
/*成功,但返回体没数据*/
public static RespResult success(){
return success(null);
}
/*失败返回信息*/
public static RespResult error(String code,String msg){
RespResult retEro = new RespResult();
retEro.setCode(code);
retEro.setMsg(msg);
return retEro;
}
}
使用
@RestController
public class TestController {
@Resource
private ApplicationContext applicationContext;
/*测试国际化文件动静参数结合模式抛出全局异常*/
@RequestMapping("/globalError")
public RespResult globalError(){
throw new ManageException("123456","傻子","天才");
}
/*Validated校验抛出异常*/
@RequestMapping("/globalErrorVo")
public RespResult globalErrorVo(@RequestBody @Validated ReqParam reqParam) {
return new RespResult();
}
{0} 代表第一个参数, {1}代表第二个,可以有N个
测试国际化文件动静参数结合模式抛出全局异常 测试场景如图:
Validated校验抛出异常 场景如图
结束总结
如果你只复制粘贴运气好可能执行成功了,但最靠谱的就是在复制完了,脑袋过一遍代码,哪里不明白研究一下,至少出现一些基础的错误能够看懂,这样会少走很多弯路。