自定义参数校验以及统一处理结果集
Posted 野生java研究僧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义参数校验以及统一处理结果集相关的知识,希望对你有一定的参考价值。
自定义参数校验以及统一处理结果集
- 1.引入校验组件
- 2.定义统一返回结果
- 3.返回值统一包装处理
- 4.@RestControllerAdvice+ResponseBodyAdvice统一处理
- 5.@RestControllerAdvice+@ExceptionHandler处理
- 6.方法基本参数校验原理
- 7.方法级别的参数校验实现原理
1.引入校验组件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
更多校验使用方法请参考:java后端参数校验validaction(用法详解)
2.定义统一返回结果
统一返回值类型无论项目前后端是否分离都是非常必要的,方便对接接口的开发人员更加清晰地知道这个接口的调用是否成功(不能仅仅简单地看返回值是否为 null 就判断成功与否,因为有些接口的设计就是如此),使用一个状态码、状态信息就能清楚地了解接口调用情况
@Data
public class ResponseResult<T>
private static final long serialVersionUID = 5337617615451873318L;
/**
* code 200 表示业务处理成功,-200表示业务处理失败
*
* @since 1.0
**/
private String code;
/**
* 存放业务提示信息
*
* @since 1.0
**/
private String message;
/**
* subCode 10000 表示10000请求成功 -10000表示请求处理失败
*
* @since 1.0
**/
private String subCode;
/**
* 存放系统异常消息
*
* @since 1.0
**/
private String subMessage;
/**
* 存放系统异常具体错误详情
*
* @since 1.0
**/
private String errorDetails;
/**
* 响应时间
*
* @since 1.0
**/
private String responseTime;
/**
* 出错的服务mac地址
*
* @since 1.0
**/
private String mac;
/**
* 预留处理字段
*
* @since 1.0
**/
private Object option;
/**
* 执行时间
*
* @since 1.0
**/
private Long executeTime;
/**
* 响应数据
*
* @since 1.0
**/
private T data;
private static final String UNKNOWN_ERROR_CODE = "9999";
private static final String SUCCESS_CODE = "10000";
private static final String ERROR_CODE = "0";
private static final String BUSINESS_SUCCESS = "200";
private static final String BUSINESS_ERROR = "500";
private static final String SYSTEM_ERROR_MESSAGE = "系统异常,请联系管理员处理";
private static final String SYSTEM_SUCCESS_MESSAGE = "服务调用成功";
private static final String UNKNOWN_ERROR_MESSAGE = "未知异常";
private ResponseResult()
private ResponseResult(String code, String message, String subMessage, String subCode, T data, String errorDetails,Long executeTime)
this.code = code;
this.message = message;
this.subMessage = subMessage;
this.subCode = subCode;
this.data = data;
this.errorDetails = errorDetails;
// 将日期格式化为:yyyy-MM-dd HH:mm:ss
this.responseTime = DateTools.format(new Date(), DateTools.DEFAULT_DATETIME_FORMAT);
String mac = SysTemUtil.getLocalMacAddress(null);
this.mac = StringUtils.hasLength(mac) ? mac.replaceAll("-", "") : "";
this.executeTime = executeTime;
private ResponseResult(String code, String message, String subMessage, String subCode, T data, String errorDetails )
this.code = code;
this.message = message;
this.subMessage = subMessage;
this.subCode = subCode;
this.data = data;
this.errorDetails = errorDetails;
// 将日期格式化为:yyyy-MM-dd HH:mm:ss
this.responseTime = DateTools.format(new Date(), DateTools.DEFAULT_DATETIME_FORMAT);
String mac = SysTemUtil.getLocalMacAddress(null);
this.mac = StringUtils.hasLength(mac) ? mac.replaceAll("-", "") : "";
// 构造一个响应结果对象
public static <E> ResponseResult<E> build(String code, String message, String subMessage, String subCode, E data, String errorDetails)
return new ResponseResult<>(code, message, subMessage, subCode, data, errorDetails);
// 简单的成功响应不带返回值
public static <E> ResponseResult<E> success()
return build(BUSINESS_SUCCESS, "", SYSTEM_SUCCESS_MESSAGE, SUCCESS_CODE, null, "");
// 简单的成功响应
public static <E> ResponseResult<E> success(E data)
return build(BUSINESS_SUCCESS, "", SYSTEM_SUCCESS_MESSAGE, SUCCESS_CODE, data, "");
// 简单的成功响应,携带提示信息
public static <E> ResponseResult<E> success(String message, E data)
return build(BUSINESS_SUCCESS, message, SYSTEM_SUCCESS_MESSAGE, SUCCESS_CODE, data, "");
// 简单的失败响应
public static <E> ResponseResult<E> error(String subMessage)
return build(BUSINESS_ERROR, subMessage, SYSTEM_SUCCESS_MESSAGE, SUCCESS_CODE, null, "");
// 系统异常的失败响应
public static <E> ResponseResult<E> systemError(String errorDetails)
return build(BUSINESS_ERROR, SYSTEM_SUCCESS_MESSAGE, SYSTEM_ERROR_MESSAGE, SUCCESS_CODE, null, errorDetails);
// 失败响应写的异常原因
public static <E> ResponseResult<E> error(String message, String subMessage)
return build(BUSINESS_ERROR, message, subMessage, ERROR_CODE, null, "");
// 响应失败,携带数据和提示信息
public static <E> ResponseResult<E> error(String message, E data)
return build(BUSINESS_ERROR, message, SYSTEM_SUCCESS_MESSAGE, ERROR_CODE, data, "");
获取本机物理地址的工具方法:
/**
* 获取本地mac地址
* 注意:物理地址是48位,别和ipv6搞错了
* @param inetAddress
* @return 本地mac地址
*/
public static String getLocalMacAddress(InetAddress inetAddress)
if (inetAddress==null)
try
inetAddress = InetAddress.getLocalHost();
catch (UnknownHostException e)
e.printStackTrace();
throw new RuntimeException("获取主机mac地址失败");
try
//获取网卡,获取地址
byte[] mac = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < mac.length; i++)
if (i != 0)
sb.append("-");
//字节转换为整数
int temp = mac[i] & 0xff;
String str = Integer.toHexString(temp);
if (str.length() == 1)
sb.append("0").append(str);
else
sb.append(str);
return sb.toString();
catch (Exception exception)
exception.getMessage();
return "";
3.返回值统一包装处理
Spring 中提供了一个类 ResponseBodyAdvice
,能帮助我们实现上述需求
ResponseBodyAdvice
是对 Controller 返回的内容在 HttpMessageConverter
进行类型转换之前拦截,进行相应的处理操作后,再将结果返回给客户端。那这样就可以把统一包装的工作放到这个类里面。
public interface ResponseBodyAdvice<T>
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
4.@RestControllerAdvice+ResponseBodyAdvice统一处理
import com.springboot.example.bean.ResponseResult;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @RestControllerAdvice+@ExceptionHandler 异常处理器的优先级要高,如果那边处理过了是ResponseResult,直接返回
* 所以建议在 @RestControllerAdvice+@ExceptionHandler进行处理可以获取到完整的错误信息,
* @RestControllerAdvice+ResponseBodyAdvice适合对所有返回值进行第二次处理,不适合异常消息捕获
* @author compass
* @date 2023-02-05
* @since 1.0
**/
@RestControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice<Object>
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass)
return true;
@Override
public Object beforeBodyWrite(Object result, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse)
try
if (result instanceof ResponseResult)
return result;
else if ("error".equals(Objects.requireNonNull(methodParameter.getMethod()).getName()))
Map map = (Map) result;
String path = map.get("path") == null ? "" : map.get("path").toString();
Object errors = map.get("errors");
if (errors instanceof List)
List errorList = (List) errors;
StringBuilder subMessageBuffer = new StringBuilder();
StringBuilder messageBuffer = new StringBuilder();
for (Object item : errorList)
if (item instanceof FieldError)
FieldError error = (FieldError) item;
String rejectedValue = error.getRejectedValue() + "";
String field = error.getField();
String message = error.getDefaultMessage();
messageBuffer.append("请求路径:").append(path).append(";");
messageBuffer.append("校验不通过的字段名:").append(field).append(",")
.append("被拒绝的值:").append(rejectedValue).append(",")
.append("提示信息:").append(message).append(";");
subMessageBuffer.append(message).append(",");
String message = messageBuffer.toString();
String subMessage = subMessageBuffer.toString();
subMessage = subMessage.substring(0, subMessage.lastIndexOf(','));
return ResponseResult.build("10000", message, subMessage, "500", null, null);
// 处理是 @PathVariable 和 @RequestParam 参数校验
Object stats = map.get("status");
if (Integer.valueOf(500).equals(stats))
String message = "请求路径:"+map.get("path")+";"+map.get("message");
String subMessage = map.get("message")+"";
return ResponseResult.build("10000", message, subMessage, "500", null, null);
return result;
catch (Exception e)
e.printStackTrace();
return ResponseResult.error("系统异常","统一返回值处理失败");
5.@RestControllerAdvice+@ExceptionHandler处理
在 SpringMVC 中,有一个类是 RequestResponseBodyMethodProcessor,这个类有两个作用
- 用于解析 @RequestBody 标注的参数
- 处理 @ResponseBody 标注方法的返回值
解析 @RequestBoyd 标注参数的方法是 resolveArgument
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor
/**
* Throws MethodArgument以上是关于自定义参数校验以及统一处理结果集的主要内容,如果未能解决你的问题,请参考以下文章
springboot 实体参数校验 validate 抛出javax.validation.ConstraintViolationException异常 统一处理