SpringBoot2.X Kotlin系列之?dāng)?shù)據(jù)校驗(yàn)和異常處理詳解
在開(kāi)發(fā)項(xiàng)目時(shí),我們經(jīng)常需要在前后端都校驗(yàn)用戶提交的數(shù)據(jù),判斷提交的數(shù)據(jù)是否符合我們的標(biāo)準(zhǔn),包括字符串長(zhǎng)度,是否為數(shù)字,或者是否為手機(jī)號(hào)碼等;這樣做的目的主要是為了減少SQL注入攻擊的風(fēng)險(xiǎn)以及臟數(shù)據(jù)的插入。提到數(shù)據(jù)校驗(yàn)我們通常還會(huì)提到異常處理,因?yàn)闉榱税踩鹨?jiàn),后端出現(xiàn)的異常我們通常不希望直接拋到客戶端,而是經(jīng)過(guò)我們的處理之后再返回給客戶端,這樣做主要是提升系統(tǒng)安全性,另外就是給予用戶友好的提示。
定義實(shí)體并加上校驗(yàn)注解
class StudentForm() {
@NotBank(message = '生日不能為空')
var birthday: String = ""
@NotBlank(message = "Id不能為空")
var id:String = ""
@NotBlank(message = "年齡不能為空")
var age:String = ""
@NotEmpty(message = "興趣愛(ài)好不能為空")
var Interests:List<String> = Collections.emptyList()
@NotBlank(message = "學(xué)校不能為空")
var school: String = ""
override fun toString(): String {
return ObjectMapper().writeValueAsString(this)
}
}
這里首先使用的是基礎(chǔ)校驗(yàn)注解,位于javax.validation.constraints下,常見(jiàn)注解有@NotNull、@NotEmpty、@Max、@Email、@NotBank、@Size、@Pattern,當(dāng)然出了這些還有很多注解,這里就不在一一講解,想了解更多的可以咨詢查看jar包。
這里簡(jiǎn)單講解一下注解的常見(jiàn)用法:
- @NotNull: 校驗(yàn)一個(gè)對(duì)象是否為Null
- @NotBank: 校驗(yàn)字符串是否為空串
- @NotEmpty: 校驗(yàn)List、Map、Set是否為空
- @Email: 校驗(yàn)是否為郵箱格式
- @Max @Min: 校驗(yàn)Number或String是否在指定范圍內(nèi)
- @Size: 通常需要配合@Max @Min一期使用
- @Pattern: 配合自定義正則表達(dá)式校驗(yàn)
定義返回狀態(tài)枚舉
enum class ResultEnums(var code:Int, var msg:String) {
SUCCESS(200, "成功"),
SYSTEM_ERROR(500, "系統(tǒng)繁忙,請(qǐng)稍后再試"),
}
自定義異常
這里主要是參數(shù)校驗(yàn),所以定義一個(gè)運(yùn)行時(shí)異常,代碼如下:
class ParamException(message: String?) : RuntimeException(message) {
var code:Int = ResultEnums.SUCCESS.code
constructor(code:Int, message: String?):this(message) {
this.code = code
}
}
統(tǒng)一返回結(jié)構(gòu)體定義
class ResultVo<T> {
var status:Int = ResultEnums.SUCCESS.code
var msg:String = ""
var data:T? = null
constructor()
constructor(status:Int, msg:String, data:T) {
this.status = status
this.data = data
this.msg = msg
}
override fun toString(): String {
return ObjectMapper().writeValueAsString(this)
}
}
全局異常處理
這里的全局異常處理,是指請(qǐng)求到達(dá)Controller層之后發(fā)生異常處理。代碼如下:
@RestControllerAdvice
class RestExceptionHandler {
private val logger:Logger = LoggerFactory.getLogger(this.javaClass)
@ExceptionHandler(Exception::class)
@ResponseBody
fun handler(exception: Exception): ResultVo<String> {
logger.error("全局異常:{}", exception)
return ResultVo(500, "系統(tǒng)異常", "")
}
@ExceptionHandler(ParamException::class)
@ResponseBody
fun handler(exception: ParamException): ResultVo<String> {
logger.error("參數(shù)異常:{}", exception.localizedMessage)
return ResultVo(exception.code, exception.localizedMessage, "")
}
}
這里得和Java處理的方式大同小異,無(wú)疑就是更加簡(jiǎn)潔了而已。
編寫(xiě)校驗(yàn)工具
object ValidatorUtils {
private val validator = Validation.buildDefaultValidatorFactory().validator
/**
* 校驗(yàn)對(duì)象屬性
* @param obj 被校驗(yàn)對(duì)象
* @param <T> 泛型
* @return Map
</T> */
fun validate(obj: Any): Map<String, String> {
var errorMap: Map<String, String>? = null
val set = validator.validate(obj, Default::class.java)
if (CollectionUtils.isEmpty(set)) {
return emptyMap()
}
errorMap = set.map { it.propertyPath.toString() to it.message }.toMap()
return errorMap
}
/**
* 校驗(yàn)對(duì)象屬性
* @param obj 被校驗(yàn)對(duì)象
* @param <T> 泛型
* @return List
</T> */
fun validata(obj: Any): List<String> {
val set = validator.validate(obj, Default::class.java)
return if (CollectionUtils.isEmpty(set)) {
emptyList()
} else set.stream()
.filter {Objects.nonNull(it)}
.map { it.message }
.toList()
}
}
抽象校驗(yàn)方法
因?yàn)樾r?yàn)是通用的,幾乎大部分接口都需要檢驗(yàn)傳入?yún)?shù),所以我們把校驗(yàn)方法抽出來(lái)放在通用Controller層里,通用層這里不建議使用Class或者是抽象類(lèi),而是使用interface,定義如下:
@Throws(ParamException::class)
fun validate(t:Any) {
val errorMap = ValidatorUtils.validate(t).toMutableMap()
if (errorMap.isNotEmpty()) {
throw ParamException(ResultEnums.SYSTEM_ERROR.code, errorMap.toString())
}
}
這里如果有參數(shù)錯(cuò)誤就直接拋出參數(shù)異常,然后交給全局異常處理器來(lái)捕獲。
Controller層編寫(xiě)
@PostMapping("/student")
fun create(@RequestBody studentForm: StudentForm): ResultVo<StudentDTO> {
this.validate(studentForm)
val studentDTO = StudentDTO()
BeanUtils.copyProperties(studentForm, studentDTO)
return ResultVo(200, "", studentDTO)
}
1.傳入一個(gè)空對(duì)象: 返回結(jié)果:
{
"status": 500,
"msg": "{school=學(xué)校不能為空, id=Id不能為空, age=年齡不能為空, Interests=興趣愛(ài)好不能為空}",
"data": ""
}
自定義校驗(yàn)規(guī)則
本篇文章開(kāi)始之前我們提到過(guò)@Pattern,這個(gè)注解主要是方便我們定義自己的校驗(yàn)規(guī)則,假如我這里需要校驗(yàn)前端傳入的生日,是否符合我所需要的格式,如下所示:
@NotBlank(message = "生日不能為空")
@Pattern(regexp="^(19|20)\\d{2}-(1[0-2]|0?[1-9])-(0?[1-9]|[1-2][0-9]|3[0-1])$", message="不是生日格式")
var birthday: String = ""
這里的校驗(yàn)邏輯可能不完善,大家使用的時(shí)候需要注意。
修改完成后我再次請(qǐng)求
請(qǐng)求示例
空值
入?yún)ⅲ?
{
"age": "10",
"id": "1",
"school": "學(xué)校",
"interests": ["戶外運(yùn)動(dòng)"],
"birthday": ""
}
出參:
{
"status": 500,
"msg": "{birthday=生日不能為空}",
"data": ""
}
錯(cuò)誤參數(shù)
入?yún)ⅲ?
{
"age": "10",
"id": "1",
"school": "學(xué)校",
"interests": ["戶外運(yùn)動(dòng)"],
"birthday": "1989-20-20"
}
出參:
{
"status": 500,
"msg": "{birthday=不是生日格式}",
"data": ""
}
正確示例
入?yún)ⅲ?
{
"age": "10",
"id": "1",
"school": "學(xué)校",
"interests": ["戶外運(yùn)動(dòng)"],
"birthday": "1999-01-01"
}
出參:
{
"status": 200,
"msg": "",
"data": {
"id": "1",
"birthday": "1999-01-01",
"age": "10",
"school": "學(xué)校"
}
}
本章內(nèi)容就到此結(jié)束了,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
聊聊Spring data jpa @query使用原生SQl,需要注意的坑
這篇文章主要介紹了Spring data jpa@query使用原生SQl,需要注意的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
SpringCloud使用Zookeeper作為配置中心的示例
這篇文章主要介紹了SpringCloud使用Zookeeper作為配置中心的示例,幫助大家更好的理解和學(xué)習(xí)使用SpringCloud,感興趣的朋友可以了解下2021-04-04
簡(jiǎn)單實(shí)現(xiàn)java數(shù)獨(dú)游戲
這篇文章主要教大家如何簡(jiǎn)單實(shí)現(xiàn)java數(shù)獨(dú)游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
SpringBoot中并發(fā)定時(shí)任務(wù)的實(shí)現(xiàn)、動(dòng)態(tài)定時(shí)任務(wù)的實(shí)現(xiàn)(看這一篇就夠了)推薦
這篇文章主要介紹了SpringBoot并發(fā)定時(shí)任務(wù)動(dòng)態(tài)定時(shí)任務(wù)實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
javaweb圖書(shū)商城設(shè)計(jì)之圖書(shū)模塊(4)
這篇文章主要介紹了javaweb圖書(shū)商城設(shè)計(jì)之圖書(shū)模塊的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
使用Java如何將圖片轉(zhuǎn)成Base64編碼,并壓縮至40k
這篇文章主要介紹了使用Java如何將圖片轉(zhuǎn)成Base64編碼,并壓縮至40k問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
SpringBoot+jpa配置如何根據(jù)實(shí)體類(lèi)自動(dòng)創(chuàng)建表
這篇文章主要介紹了SpringBoot+jpa配置如何根據(jù)實(shí)體類(lèi)自動(dòng)創(chuàng)建表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

