天天看點

Spring 資料校驗 @Valid 統一異常處理以及統一日志列印

  • controller 測試類
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 版本資訊
 */
@RestController
public class TestController {
    @RequestMapping("/test/valid")
    @ResponseJson
    public Map<String, String> test(@Valid TestReq testReq,Errors error) {
        return null;
    }

}

           
  • 傳入的bean

    TestReq

/**
 * @author wangteng
 * @desc
 * @date 2018/12/10 19:57
 */
@Data
public class TestReq {
    @NotNull(message = "手機号不能為空")
    @Length(max = 12,min = 0)
    private String mobile;
}

           
  • AOP處理

    WebLogAndParamValidateAspect

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

/**
 * 參數驗證以及請求日志列印 ,當請求參數實作com.payeco.base.common.model.BaseRequestParam接口會自動列印入參。
 * 如敏感資訊請求參數請不要實作該接口而是自行按需列印入參
 * @author xianyongjie
 */

@Slf4j
@Aspect
@Component
public class WebLogAndParamValidateAspect {

  public static final Logger accessLog = LoggerFactory.getLogger("request_access_log");

  /**
   * 配置切入點,該方法無方法體,主要為友善同類中其他方法使用此處配置的切入點
   */
  @Pointcut("execution(* .....controller..*(..))")
  public void aspect() {
  }

  /**
   * 配置前置通知,使用在方法aspect()上注冊的切入點,同時接受JoinPoint切入點對象,可以沒有該參數
   * @param pjp
   * @return
   * @throws Throwable
   */
  @Around("aspect()")
  public Object around(ProceedingJoinPoint pjp) throws Throwable {
    this.before(pjp);
    Object rs = pjp.proceed();
    return rs;
  }

  /**
   *
   * @param pjp
   */
  private void before(ProceedingJoinPoint pjp) {
    Object[] params = pjp.getArgs();
    Errors errors = null;
    for (Object param : params) {
      if (param instanceof Errors) {
        errors = (Errors) param;
      }
    }

    // 接收到請求,記錄請求内容
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
        .getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    String url = request.getRequestURL().toString();

    RequestLog requestLog = new RequestLog();
    requestLog.setHeader(this.getHeader(request));
    requestLog.setUrl(url);

    //記錄請求日志
    this.requestLog(requestLog);

    //驗證參數合法性
    this.validateParams(errors);

  }

  /**
   * 列印請求日志
   */
  private void requestLog(RequestLog requestLog) {
    accessLog.info(JSON.toJSONString(requestLog));
  }

  /**
   * 參數驗證
   */
  private void validateParams(Errors errors) {
    if (errors != null && errors.hasErrors()) {
      List<ObjectError> errorList = errors.getAllErrors();
      StringBuilder sb = new StringBuilder();
      FieldError fieldError = (FieldError) errorList.get(0);
      sb.append(fieldError.getDefaultMessage());
      //抛出自定義異常
      throw new BusinessException(BusinessRetCode.REQUEST_PARAM_ILLEGAL.getErrorCode(),
              sb.toString().trim());
    }
  }

  /**
   * 擷取請求頭
   */
  public Map<String, Object> getHeader(HttpServletRequest request) {
    Enumeration<String> e = request.getHeaderNames();
    Map<String, Object> header = new HashedMap();
    while (e.hasMoreElements()) {
      String key = e.nextElement();
      header.put(key, request.getHeader(key));
    }
    return header;
  }

  /**
   * 請求日志記錄
   */
  @Data
  class RequestLog {

    @JSONField(ordinal = 1)
    String url;
    @JSONField(ordinal = 2)
    Map<String, Object> header;
    String ip;
    String clientName;
  }
}

           
  • BusinessException
public class BusinessException extends RuntimeException {

    private String errorCode;

    public BusinessException(ErrorDefinition errorDefinition) {
        super(errorDefinition.getMessage());
        this.errorCode = errorDefinition.getErrorCode();
    }

    @SuppressWarnings("unchecked")
    public BusinessException(ErrorDefinition errorDefinition, Object... formatMessage) {
        super(String.format(errorDefinition.getMessage(), formatMessage));
        this.errorCode = errorDefinition.getErrorCode();
    }

    public BusinessException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public BusinessException(String message) {
        super(message);
        this.errorCode = CommonRetCode.COM_FAIL.getErrorCode();
    }

    public String getErrorCode() {
        return errorCode;
    }

}
           

繼續閱讀