天天看點

Controller 層參數校驗方案

思路

Controller層有兩種校驗場景

  1. 單個參數的校驗
// 使用者登入 Controller 方法
@PostMapping("/login")
public Message login(String verifyCode,String account,String password){
  //....       
  return null;
}
           
  1. 實體類的校驗

    實體類User裡,有多個字段需要校驗,比如使用者名不能為空,密碼不能為空等等

// 添加使用者 Controller 方法
@PostMapping("/add")
public Message addUser(User user){
  //....       
  return null;
}
           

對于第一種場景,我們可以使用GET參數校驗(@RequestParam參數校驗)來友善校驗參考:

GET參數校驗(@RequestParam參數校驗)

第二種場景,則結合AOP使用請求參數校驗。參考:

[Spring] Web層AOP方式進行參數校驗 [SpringMVC] Web層注解式參數校驗

下面介紹GET參數校驗的實作方法。

環境

注意,Spring boot 内已經內建了Hibernate validator

  • Spring boot
  • Hibernate validator
  • lombok

Hibernate 參數校驗 - GET參數校驗模式 的實作

  • 編寫配置類
package com.spz.demo.security.config;

import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

/**
 * 配置 Hibernate 參數校驗
 * 參考:https://blog.csdn.net/u010454030/article/details/53009327
 */
@Slf4j(topic = "SYSTEM_LOG")//日志子產品
@Configuration
@EnableAutoConfiguration
public class ValidatorConfig {
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        postProcessor.setValidator(validator());//快速校驗,隻要有錯馬上傳回
        return postProcessor;
    }

    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
    }

}

           
  • 編寫驗證錯誤資訊提示

    校驗未通過時,将抛出ConstraintViolationException異常,此時需要在異常處理方法裡擷取錯誤資訊,并進行傳回

package com.spz.demo.security.config;

import com.spz.demo.security.bean.Message;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Set;

@ControllerAdvice
@Component
public class GlobalExceptionHandler {

    /**
     * hibernate 參數校驗出錯會抛出 ConstraintViolationException 異常
     * 在此方法中處理,将錯誤資訊輸出
     * @param exception
     * @return
     */
    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handle(ValidationException exception) {
        String errorInfo = "";
        if(exception instanceof ConstraintViolationException){
            ConstraintViolationException exs = (ConstraintViolationException) exception;

            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();

            for (ConstraintViolation<?> item : violations) {
                errorInfo = errorInfo + "[" + item.getMessage() + "]";
            }
        }
        return new Message().setErrorMessage(errorInfo);
    }
}

           
  • Message類是包裝請求傳回,一般在Controller層使用,用于傳回json格式資料
package com.spz.demo.security.bean;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.spz.demo.security.common.MessageCode;
import lombok.Data;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * 請求響應Bean
 * 使用JSON包裝請求傳回,使用jackson庫
 *
 * @author spz
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Message implements Serializable {
    private int code ;
    private String message ;
    private Map<String,Object> data = new HashMap<String, Object>();

    /**
     * 自定義傳回
     * @param code
     * @param message
     * @return
     */
    public Message setMessage(int code, String message){
        this.code = code;
        this.message = message;
        return this;
    }

    /**
     * 傳回成功
     * @return
     */
    public Message setSuccessMessage(){
        this.code = MessageCode.SUCCESS ;
        this.message = "操作成功" ;
        return this;
    }

    /**
     * 傳回成功
     * @param message
     * @return
     */
    public Message setSuccessMessage(String message){
        this.code = MessageCode.SUCCESS ;
        this.message = message ;
        return this;
    }

    /**
     * 傳回錯誤
     * @param message
     * @return
     */
    public Message setErrorMessage(String message){
        this.code = MessageCode.ERROR ;
        this.message = message ;
        return this;
    }

    /**
     * 傳回警告
     * @param message
     * @return
     */
    public Message setWarnMessage(String message){
        this.code = MessageCode.WARN ;
        this.message = message ;
        return this;
    }

    /**
     * 傳回未登入
     * @param message
     * @return
     */
    public Message setNoLoginMessage(String message){
        this.code = MessageCode.NO_LOGIN ;
        this.message = message ;
        return this;
    }

    /**
     * 傳回沒有權限
     * @param message
     * @return
     */
    public Message setPermissionDeniedMessage(String message){
        this.code = MessageCode.PERMISSION_DENIED ;
        this.message = message ;
        return this;
    }
}

           
  • Controller 層使用校驗
package com.spz.demo.security.controller;

import com.spz.demo.security.bean.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotEmpty;

/**
 * 使用者控制器
 *
 * @author spz
 */
@Slf4j(topic = "USER_LOG")
@RestController
@RequestMapping("/user")
@Validated//需要使用Hibernate非實體類參數校驗,需要加入此注解
public class UserController {

    @Autowired
    StringRedisTemplate redis;

    /**
     * 使用者登入
     * @return
     */
    @PostMapping("/login")
    public Message login(@NotEmpty(message = "賬号不能為空") String account,
                         @NotEmpty(message = "密碼不能為空") String password,
                         @NotEmpty(message = "驗證碼不能為空") String verifyCode){
        //....
        return new Message().setSuccessMessage();
    }

}

           

使用時,發送使用者登入請求,如果參數校驗失敗,将傳回錯誤資訊:

{
  "code": 5000,
  "message": "[驗證碼不能為空]",
  "data": { }
}
           

Hibernate 參數校驗 - 實體類校驗模式的實作