帮你少写一大半参数校验代码的小技巧
介紹
幾乎每個web網站都會對用戶提交的參數進行校驗,前端要做,后端也要做。防止用戶直接通過接口調用的方式來請求或保存數據,從而導致產生臟數據等其他嚴重的后果。
因為有些校驗的邏輯也很繁瑣,為了減輕開發者的負擔,Java發布了 JSR303/JSR-349數據校驗規范
JSR303 是一項標準,JSR-349 是其的升級版本,添加了一些新特性,他們規定一些校驗規范即校驗注解,如 @Null,@NotNull,@Pattern,他們位于 javax.validation.constraints 包下,只提供規范不提供實現。
而 hibernate validation 是對這個規范的實踐(不要將 hibernate 和數據庫 orm 框架聯系在一起),他提供了相應的實現,并增加了一些其他校驗注解,如 @Length,@Range 等等,他們位于 org.hibernate.validator.constraints 包下。
而萬能的 spring 為了給開發者提供便捷,對 hibernate validation 進行了二次封裝,顯示校驗 validated bean 時,你可以使用 spring validation 或者 hibernate validation,而 spring validation 另一個特性,便是其在 springmvc 模塊中添加了自動校驗,并將校驗信息封裝進了特定的類中。這無疑便捷了我們的 web 開發
在spring boot項目中只要加入如下依賴即可使用校驗注解
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency>查看子依賴會發現有如下依賴
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId> </dependency>JSR提供的部分校驗注解如下
| @Null | 被注釋的元素必須為 null |
| @NotNull | 被注釋的元素必須不為 null |
| @AssertTrue | 被注釋的元素必須為 true |
| @AssertFalse | 被注釋的元素必須為 false |
| @Min | 被注釋的元素必須是一個數字,其值必須大于等于指定的最小值 |
| @Max | 被注釋的元素必須是一個數字,其值必須小于等于指定的最大值 |
| @Pattern | 被注釋的元素必須符合指定的正則表達式 |
| 被注釋的元素必須是電子郵箱地址 |
經常被搞混的3個注解
| @NotNull | 不能為null,但可以為empty |
| @NotEmpty | 不能為null,而且長度必須大于0 |
| @NotBlank | 只能作用在String上,不能為null,而且調用trim()后,長度必須大于0 |
我來舉一個org.apache.commons.lang3.StringUtils中的例子,你就能理解NotBlank的意思了,如下斷言都能測試通過
assertEquals(true,?StringUtils.isBlank(null)); assertEquals(true,?StringUtils.isBlank("")); //?空格 assertEquals(true,?StringUtils.isBlank("?")); //?回車 assertEquals(true,?StringUtils.isBlank("????"));改造一個注冊的接口
先定義一下狀態枚舉類
public?enum?ResponseCode?{SUCCESS(0,?"成功"),ERROR(1,?"失敗"),ILLEGAL_ARGUMENT(2,?"參數錯誤"),EMPTY_RESULT(3,?"結果為空"),NEED_LOGIN(10,?"需要登錄");private?final?int?code;private?final?String?desc;ResponseCode(int?code,?String?desc)?{this.code?=?code;this.desc?=?desc;}public?int?getCode()?{return?code;}public?String?getDesc()?{return?desc;} }定義項目的返回對象
@Data /**?注解的作用是序列化json時,如果是null對象,key也會消失?*/ @JsonInclude(JsonInclude.Include.NON_NULL) public?class?ServerResponse?implements?Serializable?{/**?狀態值?**/private?int?status;/**?描述?**/private?String?msg;/**?數據?**/private?Object?data;public?ServerResponse(int?status,?String?msg)?{this.status?=?status;this.msg?=?msg;}public?static?ServerResponse?success()?{return?new?ServerResponse(ResponseCode.SUCCESS.getCode(),?ResponseCode.SUCCESS.getDesc());}public?static?ServerResponse?illegalArgument(String?msg)?{return?new?ServerResponse(ResponseCode.ILLEGAL_ARGUMENT.getCode(),?msg);} }注冊接口,這里省略了一部分校驗
@RequestMapping("regist") public?ServerResponse?regist(@RequestParam("name")?String?name,@RequestParam("phone")?String?phone,@RequestParam("phone")?String?email)?{if?(StringUtils.isBlank(name))?{return?ServerResponse.illegalArgument("用戶名不能為空");}//?其他一堆校驗過程,調用servicereturn?ServerResponse.success(); }當不滿足條件時返回如下
{"status":?2,"msg":?"用戶名不能為空" }當參數較多,校驗的邏輯也越來越多,這時可以直接將前端傳過來參數直接轉為對象
@Data public?class?Student?{@NotBlank(message?=?"用戶名不能為空")private?String?name;@NotBlank(message?=?"手機號不能為空")@Pattern(regexp?=?"^1(3|4|5|7|8)\\d{9}$",?message?=?"手機號碼格式錯誤")private?String?phone;@NotBlank(message?=?"郵箱不能為空")@Email(message?=?"郵箱格式錯誤")private?String?email; }@RequestMapping("regist") public?ServerResponse?regist(@Valid?Student?student,?BindingResult?bindingResult)?{if?(bindingResult.hasErrors())?{List<FieldError>?errorList?=?bindingResult.getFieldErrors();//?list不為空if?(CollectionUtils.isNotEmpty(errorList))?{return?ServerResponse.illegalArgument(errorList.get(0).getDefaultMessage());}}//?調用servicereturn?ServerResponse.success(); }
代碼將不滿足條件的字段的描述取一個出來返回,類似如下。當都滿足時才會返回成功
{"status":?2,"msg":?"用戶名不能為空" }需要注意的地方如下
參數 Student 前需要加上@Valid或@Validated 注解(下文說這2個注解的區別),表明需要 spring 對其進行校驗,而校驗的信息會存放到其后的 BindingResult 中。注意,必須相鄰,如果有多個參數需要校驗,形式可以如下。foo(@Validated Student student, BindingResult studentBindingResult ,@Validated Bar bar, BindingResult barBindingResult); 即一個校驗類對應一個校驗結果。
@Validated比@Valid的功能更強大
@Validated比@Valid的功能更強大,主要體現在@Validated可以進行分組校驗和嵌套校驗。
假如我們還要求學生填寫父親和母親的名字(并且不能為空),而后端的設計中父親和母親的名字被放在另一個類Relation中,這就要求我們能進行嵌套校驗。
我們要做如下2個事情
@Data public?class?Student?{@NotBlank(message?=?"用戶名不能為空")private?String?name;@NotBlank(message?=?"手機號不能為空")@Pattern(regexp?=?"^1(3|4|5|7|8)\\d{9}$",?message?=?"手機號碼格式錯誤")private?String?phone;@NotBlank(message?=?"郵箱不能為空")@Email(message?=?"郵箱格式錯誤")private?String?email;@NotNull(message?=?"父母名字不能為空")@Validprivate?Relation?relation; }@Data public?class?Relation?{@NotBlank(message?=?"父親的姓名不能為空")private?String?fatherName;@NotBlank(message?=?"父親的姓名不能為空")private?String?motherName; }
那Relation類的fatherName屬性如何被賦值呢?
xxxx?relation.fatherName=li&relation.motherName=liu有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的帮你少写一大半参数校验代码的小技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu14.04下mysql数据库
- 下一篇: 一上来,就问原理,问上亿(MySQL)大