framework-core
大约 5 分钟framework
git地址:
http://10.16.202.103:8089/component/component-ser/gosci-tech-framework-core
异常处理
异常基础信息接口
返回的异常由异常编码和具体消息组成。
public interface IErrorCode extends Serializable {
/**
* 返回的code码
* @return code
*/
int getCode();
/**
* 返回的消息
* @return 消息
*/
String getMessage();
}
系统内置异常枚举
通过定义枚举定义系统内置异常。
@Getter
@AllArgsConstructor
public enum BaseErrorCode implements IErrorCode {
/** 公共错误 */
PARAM_MISS_ERROR(BaseErrorCode.PARAM_MISS_CODE, "缺少必要的请求参数"),
PARAM_TYPE_ERROR(BaseErrorCode.PARAM_TYPE_ERROR_CODE, "请求参数类型错误"),
PARAM_BIND_ERROR(BaseErrorCode.PARAM_BIND_ERROR_CODE, "请求参数绑定错误"),
PARAM_VALID_ERROR(BaseErrorCode.PARAM_VALID_ERROR_CODE, "请求参数格式不合规则"),
PARAM_REPEAT_ERROR(BaseErrorCode.PARAM_REPEAT_ERROR_CODE, "请求参数重复"),
METHOD_NOT_SUPPORTED_ERROR(BaseErrorCode.METHOD_NOT_SUPPORTED_CODE, "不支持当前请求方法"),
MEDIA_TYPE_NOT_SUPPORTED_ERROR(BaseErrorCode.MEDIA_TYPE_NOT_SUPPORTED_CODE, "不支持当前媒体类型"),
MEDIA_TYPE_NOT_ACCEPT_ERROR(BaseErrorCode.MEDIA_TYPE_NOT_ACCEPT_CODE, "不接受的媒体类型"),
DATA_NOT_EXIST_ERROR(BaseErrorCode.DATA_NOT_EXIST_CODE, "数据不存在"),
DATA_EXISTED_ERROR(BaseErrorCode.DATA_EXISTED_CODE, "数据已存在"),
DATA_ADD_ERROR(BaseErrorCode.DATA_ADD_FAILED_CODE, "数据添加失败,请重试或联系管理员!"),
DATA_UPDATE_ERROR(BaseErrorCode.DATA_UPDATE_FAILED_CODE, "数据更新失败,请重试或联系管理员!"),
DATA_DELETE_ERROR(BaseErrorCode.DATA_DELETE_FAILED_CODE, "数据删除失败,请重试或联系管理员!"),
DATA_IMPORT_ERROR(BaseErrorCode.DATA_IMPORT_FAILED_CODE, "数据导入失败,请重试或联系管理员!"),
DATA_EXPORT_ERROR(BaseErrorCode.DATA_EXPROT_FAILED_CODE, "数据导出失败,请重试或联系管理员!"),
ACCESS_DEFINED_ERROR(BaseErrorCode.ACCESS_DEFINED_FAILED_CODE, "非法访问,没有认证!"),
UNAUTHORIZED_ERROR(BaseErrorCode.UNAUTHORIZED_FAILED_CODE, "权限不够!");
/***********通用 异常 code*******/
/**
* 缺少必要的请求参数
*/
public static final int PARAM_MISS_CODE = 10001;
/**
* 请求参数类型错误
*/
public static final int PARAM_TYPE_ERROR_CODE = 10002;
/**
* 请求参数绑定错误
*/
public static final int PARAM_BIND_ERROR_CODE = 10003;
/**
* 参数校验失败
*/
public static final int PARAM_VALID_ERROR_CODE = 10004;
/**
* 请求参数绑定错误
*/
public static final int PARAM_REPEAT_ERROR_CODE = 10005;
/**
* 不支持当前请求方法
*/
public static final int METHOD_NOT_SUPPORTED_CODE = 10006;
/**
* 不接受的文件类型
*/
public static final int MEDIA_TYPE_NOT_ACCEPT_CODE = 10007;
/**
* 不支持当前文件类型
*/
public static final int MEDIA_TYPE_NOT_SUPPORTED_CODE = 10008;
/***********通用 数据 code*******/
/**
* 数据不存在
*/
public static final int DATA_NOT_EXIST_CODE = 20001;
/**
* 数据已存在
*/
public static final int DATA_EXISTED_CODE = 20003;
/**
* 数据添加失败
*/
public static final int DATA_ADD_FAILED_CODE = 20004;
/**
* 数据更新失败
*/
public static final int DATA_UPDATE_FAILED_CODE = 20005;
/**
* 数据删除失败
*/
public static final int DATA_DELETE_FAILED_CODE = 20006;
/**
* 数据导出失败
*/
public static final int DATA_IMPORT_FAILED_CODE = 20007;
/**
* 数据导出失败
*/
public static final int DATA_EXPROT_FAILED_CODE = 20008;
// -----------------通用 权限 code-----------------
/**
* 非法访问,没有认证
*/
public static final int ACCESS_DEFINED_FAILED_CODE = 30001;
/**
* 权限不够
*/
public static final int UNAUTHORIZED_FAILED_CODE = 30002;
/**
* code编码
*/
final int code;
/**
* 中文信息描述
*/
final String message;
}
自定义异常
抛出异常时使用throw new BizException()
的方式,内置了多种构造方法。
public class BizException extends RuntimeException
{
private static final long serialVersionUID = 1L;
private Integer code;
private final String message;
public BizException(IErrorCode errorCode) {
super(errorCode.getMessage());
super.fillInStackTrace();
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
public BizException(String message)
{
this.message = message;
}
public BizException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public BizException(String message, Throwable e)
{
super(message, e);
this.message = message;
}
@Override
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
}
全局异常捕获
@RestControllerAdvice
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Slf4j
public class ValidationAdvice {
@ExceptionHandler(UnauthorizedException.class)
@ResponseBody
public Result unauthorizedException(UnauthorizedException e) {
//获取异常信息,获取异常堆栈的完整异常信息
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
//日志输出异常详情
log.error(sw.toString());
String message = e.getMessage();
if (!StrUtil.isBlank(message)) {
return Result.failed(message);
}
return Result.failed(SystemCodeEnum.UNAUTHORIZED);
}
@ExceptionHandler(BizException.class)
@ResponseBody
public Result bizException(BizException e) {
//获取异常信息,获取异常堆栈的完整异常信息
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
//日志输出异常详情
log.error(sw.toString());
return Result.failed(e.getMessage());
}
/*
* 验证注解如果验证不通过会抛 ConstraintViolationException 异常
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public Result handler(ConstraintViolationException e) {
StringBuffer errorMsg = new StringBuffer();
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
violations.forEach(x -> errorMsg.append(x.getMessage()).append(";"));
return Result.failed(errorMsg.toString());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Result handler(MethodArgumentNotValidException e) {
StringBuffer sb = new StringBuffer();
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
allErrors.forEach(msg -> sb.append(msg.getDefaultMessage()).append(";"));
return Result.failed(sb.toString());
}
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public Result handler(MissingServletRequestParameterException e) {
String parameterName = e.getParameterName();
return Result.failed("缺少请求参数:" + parameterName);
}
@ExceptionHandler(SignatureException.class)
@ResponseBody
public Result handler(SignatureException e) {
return Result.failed("验签失败");
}
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handler(Exception e) {
//获取异常信息,获取异常堆栈的完整异常信息
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
//日志输出异常详情
log.error(sw.toString());
return Result.failed("服务异常,请稍后再试");
}
}
使用演示:
@GetMapping(value="/get/{id}")
public Result<SystemUserVO> ex(
@PathVariable("id") Integer id ) {
throw new BizException("未找到用户",20000);
}
接口返回结果:
{
"msg": "未找到用户",
"code": 500,
"data": null,
"success": false
}
Knife4j
Knife4j 是为 Java MVC 框架集成 Swagger 生成 Api 文档的增强解决方案,前身是 swagger-bootstrap-ui
,致力于 springfox-swagger 的增强 UI 实现。
knife4j 为了契合微服务的架构发展,由于原来 swagger-bootstrap-ui
采用的是后端Java 代码 + 前端 UI混合打包的方式,在微服务架构下显的很臃肿,因此项目正式更名为 knife4j,更名后主要专注的方面如下:
- 后端 Java 代码以及前端 UI 模块进行了分离,在微服务架构下使用更加灵活
- 提供专注于 Swagger 的增强解决方案,不同于只是单纯增强前端 UI 部分
framework-core包中已经集成好了knife4j,项目中就不需要再引入swagger了,只需要在项目的配置文件中配置一些必要信息,如:
knife:
enable: true
basePackage: com.gosci.tech.engine
title: 海洋数据引擎API文档
description: 海洋数据引擎API文档
version: 1.0.0
contact:
name: Hydra
url: http://10.16.202.109:7777/
email: 765666922@qq.com
说明:
enable
只有为true
时才会启用knife4jbasePackage
定义扫包路径,只有这个路径下的加了@Api
注解的才会被knife4j生成接口文档
用户信息
使用TransmittableThreadLocal
保存当前线程的用户信息,保存的信息如下:
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuthUser {
@NotNull(message = "用户id不可为空")
private Long userId;
@NotEmpty(message = "用户名不可为空")
private String userName;
}
在项目中,可根据实际认证方式获取用户信息,然后注入。例如:
LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get(KeyConstants.USER + userId);
//...
AuthUser authUser = new AuthUser();
authUser.setUserId(loginUser.getSysUser().getUserId());
authUser.setUserName(loginUser.getSysUser().getUserName());
AuthContextHolder.getInstance().setContext(authUser);
使用:
AuthUser authUser = AuthContextHolder.getInstance().authUser();
启动日志增强
打印激活的配置文件数量,项目运行地址以及接口文档地址信息: