Spring Boot validator參數驗證restful自定義錯誤碼響應

關於spring web應用中關於如何使用 Bean Validation API和hibernate-validator的文章已經不少,本文就再也不重複敘述,今天要介紹的重點是在SpringBoot restful服務中如何根據不一樣驗證錯誤響應不一樣的自定義錯誤碼。下面直接上代碼。java

1、定義restful統一結果返回

阿里java開發手冊中定義的一段參考【「對於公司外的 http/api 開放接口必須使用「錯誤碼」; 而應用內部推薦異常拋出;跨應用間 RPC 調用優先考慮使用 Result 方式,封裝 isSuccess()方法、 「錯誤碼」、 「錯誤簡短信息」。】。所以這裏也定義個返回結構。git

public class CommonResult<T> implements Serializable {

	/**
	 * serialVersionUID:.
	 */
	private static final long serialVersionUID = -7268040542410707954L;

	/**
	 * 是否成功
	 */
	private boolean success = false;

	/**
	 * 返回信息
	 */
	private String message;

	/**
	 * 裝在數據
	 */
	private T data;

	/**
	 * 錯誤代碼
	 */
	private String code;

	/**
	 * 默認構造器
	 */
	public CommonResult() {

	}

	/**
	 * @param success 是否成功
	 * @param message 返回的消息
	 */
	public CommonResult(boolean success, String message) {
		this.success = success;
		this.message = message;
	}

	/**
	 * @param success 是否成功
	 */
	public CommonResult(boolean success) {
		this.success = success;
	}

	/**
	 * @param code    error code
	 * @param message success or error messages
	 */
	public CommonResult(String code, String message) {
		this.code = code;
		this.message = message;
	}

	/**
	 * @param success 是否成功
	 * @param message 消息
	 * @param data    數據
	 */
	public CommonResult(boolean success, String message, T data) {
		this.success = success;
		this.message = message;
		this.data = data;
	}
    //省略get set
}

2、定義一個錯誤碼枚舉

在有須要國際化的項目,固然選擇經過i18n來配置更好,此處爲了簡單直接採用枚舉定義。這裏定義的錯誤僅供參考,不一樣公司每一個應用在實際狀況下可能都不大同樣。github

/**
 * 錯誤代碼枚舉類
 *
 */
public enum ErrorCodeEnum {

    SUCCESS("0000", "success"),

    PARAM_EMPTY("1001", "必選參數爲空"),

    PARAM_ERROR("1002", "參數格式錯誤"),

    UNKNOWN_ERROR("9999", "系統繁忙,請稍後再試....");

    private String code;

    private String desc;

    ErrorCodeEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return this.code;
    }


    public String getDesc() {
        return desc;
    }

     @Override
    public String toString() {
        return "ErrorCodeEnum{" +
                "code='" + code + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

3、靜態封裝CommonResult

靜態封裝CommonResult主要是方便在項目中快速根據邏輯寫返回結果代碼。web

/**
 * 公共響應結果成功失敗的靜態方法調用
 *
 */
public class ResultUtil {


    /**
     * return success
     *
     * @param data
     * @return
     */
    public static <T> CommonResult<T> returnSuccess(T data) {
        CommonResult<T> result = new CommonResult();
        result.setCode(ErrorCodeEnum.SUCCESS.getCode());
        result.setSuccess(true);
        result.setData(data);
        result.setMessage(ErrorCodeEnum.SUCCESS.getDesc());
        return result;
    }

    /**
     * return error
     *
     * @param code error code
     * @param msg  error message
     * @return
     */
    public static CommonResult returnError(String code, String msg) {
        CommonResult result = new CommonResult();
        result.setCode(code);
        result.setData("");
        result.setMessage(msg);
        return result;

    }

    /**
     * use enum
     *
     * @param status
     * @return
     */
    public static CommonResult returnError(ErrorCodeEnum status) {
        return returnError(status.getCode(), status.getDesc());
    }
}

4、定義BaseController來處理驗證錯誤自定義錯誤碼返回

/**
 * BaseController
 *
 */
public abstract class BaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class);

    /**
     * validate params
     *
     * @param bindingResult
     * @return
     */
    protected CommonResult validParams(BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            return processBindingError(fieldError);
        }
        return ResultUtil.returnSuccess("");
    }

    /**
     * 根據spring binding 錯誤信息自定義返回錯誤碼和錯誤信息
     *
     * @param fieldError
     * @return
     */
    private CommonResult processBindingError(FieldError fieldError) {
        String code = fieldError.getCode();
        LOGGER.debug("validator error code: {}", code);
        switch (code) {
            case "NotEmpty":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage());
            case "NotBlank":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage());
            case "NotNull":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage());
            case "Pattern":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Min":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Max":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Length":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Range":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Email":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "DecimalMin":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "DecimalMax":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Size":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Digits":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Past":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Future":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            default:
                return ResultUtil.returnError(ErrorCodeEnum.UNKNOWN_ERROR);
        }
    }
}

5、驗證明例

這裏直接給出一個簡單的參數驗證例子。spring

Controller繼承上面寫的BaseControllerapi

/**
 * 關於Validator使用測試
 *
 */

@RestController
@RequestMapping("validator")
public class ValidatorTestController extends BaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ValidatorTestController.class);

    /**
     * validate驗證測試
     *
     * @param leader
     * @param bindingResult
     * @return
     */
    @PostMapping("/test")
    public CommonResult testSimpleValidate(@Valid @RequestBody Leader leader, BindingResult bindingResult) {
        LOGGER.debug("ReqParams:{}", JSON.toJSONString(leader));
        CommonResult result = validParams(bindingResult);
        if (!result.isSuccess()) {
            return result;
        }
        return ResultUtil.returnSuccess("");
    }
}

入參對象Leader代碼restful

public class Leader {

    /**
     * 姓名
     */
    @NotEmpty
    private String name;

    /**
     * 生日
     */
    @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", message = "出生日期格式不正確")
    private String birthday;

    /**
     * 年齡
     */
    @Min(value = 0)
    private Integer age;

    //省略gettes and  setters

}

這時項目已經已經徹底能夠根據驗證錯誤來返回自定義的錯誤碼和提示了。app

本例所涉及源代碼:https://github.com/shalousun/api-doc-testide

總結

在一些對外服務提供restful的應用中,根據不一樣的驗證錯誤返回實際上是避免不了的。固然實現的方式能夠有種,而本文所採用的方式相對來講簡單易懂。測試

版權申明:轉載請註明出處,不然後果自負

相關文章
相關標籤/搜索