自定义参数校验以及统一处理结果集

Posted 野生java研究僧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义参数校验以及统一处理结果集相关的知识,希望对你有一定的参考价值。

自定义参数校验以及统一处理结果集

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,这个类有两个作用

  1. 用于解析 @RequestBody 标注的参数
  2. 处理 @ResponseBody 标注方法的返回值

解析 @RequestBoyd 标注参数的方法是 resolveArgument

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor 
      /**
     * Throws MethodArgument

以上是关于自定义参数校验以及统一处理结果集的主要内容,如果未能解决你的问题,请参考以下文章

.NET Core统一参数校验异常处理结果返回功能实现

.NET Core统一参数校验异常处理结果返回功能实现

.NET Core统一参数校验异常处理结果返回功能实现

统一异常处理

springboot 实体参数校验 validate 抛出javax.validation.ConstraintViolationException异常 统一处理

SpringMvc,Springboot统一校验自定义异常全局异常处理