中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在SpringBoot中使用JSR303對后端數據進行校驗

發布時間:2021-03-08 11:29:02 來源:億速云 閱讀:326 作者:Leah 欄目:開發技術

如何在SpringBoot中使用JSR303對后端數據進行校驗?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

項目創建

首先創建一個springboot項目

使用的springboot版本為:(本文代碼以該版本為準,不同版本springboot,在下面內容會出現一些差異

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.3.9.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>

引入如下依賴

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
   <scope>runtime</scope>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
   <exclusions>
    <exclusion>
     <groupId>org.junit.vintage</groupId>
     <artifactId>junit-vintage-engine</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
  </dependency>
 </dependencies>

這個作標在新一點的springboot版本中,需要單獨引入。在老版本是默認引入的。這個是用來引入對jsr303注解的支持。

 <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
  </dependency>

接著創建一個Java Bean

package cn.jxj4869.demo.entity;

import lombok.Data;

import javax.validation.constraints.NotNull;

@Data
public class User {
 @NotNull
 private Integer id;
 private String username;
 private String password;
 private String email;
}

返回類型的JavaBean

package cn.jxj4869.demo.entity;

import java.util.HashMap;

public class R extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public R() {
		put("code", 0);
		put("msg", "success");
	}


	public static R error(int code, String msg) {
		R r = new R();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static R ok(String msg) {
		R r = new R();
		r.put("msg", msg);
		return r;
	}

	public R put(String key, Object value) {
		super.put(key, value);
		return this;
	}
}

創建一個controller。

index方法用來跳轉到首頁。

package cn.jxj4869.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
public class UserController {

 @RequestMapping("/")
 public String index(){
  return "index";
 }
}

首頁代碼放到resources/templates目錄下

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
 <style>
  div{
   margin-top: 50px;
  }
 </style>
</head>
<body>

<div>
 新增表單
 <br><br>
 <form method="post">
  <label>用戶名</label>
  <input type="text" name="username"/>
  <br>
  <label>密碼</label>
  <input type="text" name="password"/>
  <br>
  <label>郵箱</label>
  <input type="email" name="email"/>
  <br>
  <input type="submit" value="提交"/>
 </form>
</div>
<div>
 更新表單
 <br><br>
 <form method="post">
  <input type="hidden" name="id" value="1">
  <label>用戶名</label>
  <input type="text" name="username"/>
  <br>
  <label>密碼</label>
  <input type="text" name="password"/>
  <br>
  <label>郵箱</label>
  <input type="email" name="email"/>
  <br>
  <input type="submit" value="提交"/>
 </form>
</div>

</body>
</html>

傳統的檢驗方式

要在后端進行數據校驗,傳統的校驗方式在controller層接受數據后,按照要求對數據進行校驗

比如要接收一個user bean對象。

現在要對user對象中的username屬性進行非空校驗,password屬性進行非空校驗和長度校驗。

 @PostMapping("/user")
 @ResponseBody
 public R user1(User user) throws Exception {
  if(StringUtils.isEmpty(user.getUsername())) {
   return R.error(400,"username不能為空");
  }
  if(StringUtils.isEmpty(user.getPassword())||user.getPassword().length()>8||user.getPassword().length() <4) {
   return R.error(400,"password無效");
  }
  return null;
 }

如果有多個方法都需要接受user對象, 而且要校驗的屬性可能不止usernamepassword這兩個屬性,如果每個方法里面都采用上面這種校驗方式的話,代碼就會很臃腫,而且不好維護,當改變了userbean的屬性,或者對校驗規則進行修改后,就得對所有的校驗代碼進行更新。 這是一件工程量很大的事。

使用JSR303

為了解決上述問題,我們可以使用JSR303提供的注解進行校驗。

JSR是Java Specification Requests的縮寫,意思是Java 規范提案。JSR303也就是第303號提案。

使用JSR303的方法很簡單,例如上面的需求,我們只需要在user的屬性上加上注解即可。

步驟如下:

1、給Bean添加校驗注解,一般是在 javax.validation.constraints這個包下,也還有一些是hibernate提供的。

2、開啟校驗功能@Valid。

3、當校驗失敗的時候,會拋出org.springframework.validation.BindException異常。

常用的校驗注解在文末

package cn.jxj4869.demo.entity;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;

@Data
public class User {

 private Integer id;
 @NotBlank
 private String username;
 @NotBlank
 @Length(min = 4,max = 8)
 private String password;
 private String email;
}

然后在controller里面的方法上,加上@Valid注解即可

 @PostMapping("/user2")
 @ResponseBody
 public R user2(@Valid User user) throws Exception {
  System.out.println(user);
  return null;
 }

當校驗失敗后,會出現如下錯誤。并且會給出默認的提示信息。

如何在SpringBoot中使用JSR303對后端數據進行校驗

自定義錯誤信息

那這個錯誤信息是怎么來的呢。

進入@NotNULL注解的代碼里面

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface NotBlank {

	String message() default "{javax.validation.constraints.NotNull.message}";
	............
 ............
 ............
}

會有一個message屬性。顯然就是指定錯誤的提示內容。而這些錯誤提示是在一個叫validationMessages.properties的文件中,用idea的搜索工具可以找到,雙擊shift鍵打開搜索。

發現有這么多validationMessages.properties的文件,而且支持國際化。

如何在SpringBoot中使用JSR303對后端數據進行校驗

打開validationMessages_zh.properties,可以看到里面定義了這么多的提示。而錯誤提示就是從這文件中獲取的。

如何在SpringBoot中使用JSR303對后端數據進行校驗

如果我們不想用默認的校驗提示信息的話,可以自己指定。

指定message的值即可。

@NotBlank(message = "用戶名不能為空")
 private String username;

如何在SpringBoot中使用JSR303對后端數據進行校驗

錯誤信息的獲取與響應

當校驗出錯時,會默認返回一個錯誤界面,或者返回錯誤提示的json數據。但默認提供的顯然不是我們想要的,如果可以拿到錯誤信息,那我們就能自定義相應數據了。

拿到錯誤信息的方式也很簡單,只要在方法中加上BindingResult result這個參數,錯誤信息就會封裝這里面。

@PostMapping("/user2")
 @ResponseBody
 public R user2(@Valid User user, BindingResult result) throws Exception {
  System.out.println(user);
  if(result.hasErrors()) { //判斷是否有錯誤
   Map<String,String> map = new HashMap<>();
   //1、獲取校驗的錯誤結果
   result.getFieldErrors().forEach((item)->{
    //FieldError 獲取到錯誤提示
    String message = item.getDefaultMessage();
    //獲取錯誤的屬性的名字
    String field = item.getField();
    map.put(field,message);
   });
   return R.error(400,"提交的數據不合法").put("data",map);
  }
  // 若沒有錯誤,則進行接下去的業務操作。
  return null;
 }

不過不推薦上面這種方式,理由同上,當校驗的地方多了,每個方法里面都加上這么個異常處理,會讓代碼很臃腫。

不知道你們是否還記得,springmvc里面有個全局的異常處理,我們可以自定義一個異常處理,在這里面統一處理異常。

統一處理BinException。這樣就可以不用在controller中去處理錯誤信息了。

package cn.jxj4869.demo.execption;

import cn.jxj4869.demo.entity.R;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice(basePackages = "cn.jxj4869.demo.controller")
public class MyExceptionControllerAdvice {

 @ExceptionHandler(value = BindException.class)
 public R handleVaildException(BindException e) {

   Map<String,String> map = new HashMap<>();
   //1、獲取校驗的錯誤結果
   e.getFieldErrors().forEach((item)->{
    //FieldError 獲取到錯誤提示
    String message = item.getDefaultMessage();
    //獲取錯誤的屬性的名字
    String field = item.getField();
    map.put(field,message);
   });
   return R.error(400,"提交的數據不合法").put("data",map);

 }
}

錯誤異常類型補充

校驗出錯的時候,會拋出兩種異常

org.springframework.validation.BindException

使用@Valid注解進行校驗的時候拋出的

org.springframework.web.bind.MethodArgumentNotValidException

使用@validated校驗的時候拋出的

在異常捕獲中加入下面這個

 @ExceptionHandler(value= MethodArgumentNotValidException.class)
 public R handleVaildException(MethodArgumentNotValidException e){

  BindingResult bindingResult = e.getBindingResult();

  Map<String,String> map = new HashMap<>();
  bindingResult.getFieldErrors().forEach((fieldError)->{
   map.put(fieldError.getField(),fieldError.getDefaultMessage());
  });
  return R.error(400,"提交的數據不合法").put("data",map);
 }

分組校驗

在不同業務場景下,校驗規則是不一樣的,比如user對象中id這個屬性,在新增的時候,這個屬性是不用填的,要為null,但是在修改的時候,id屬性是不能為null的。

可以用注解中的groups屬性來指定,在什么場合下使用改注解

@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotBlank {

	Class<?>[] groups() default { };
	........
}

首先定義兩個接口AddGroupUpdateGroup,不需要做任何實現

package cn.jxj4869.demo.valid;

public interface UpdateGroup {
}
package cn.jxj4869.demo.valid;

public interface AddGroup {
}

在user中指定group。

  • id屬性在AddGroup的時候,要為null,在UpdateGroup的時候不能為null

  • username屬性在AddGroup和Update的時候,都要進行校驗,不能為空。

  • password屬性,當校驗的時候指定分組的話,會不起作用,因為沒有給它指定校驗的分組

package cn.jxj4869.demo.entity;

import cn.jxj4869.demo.valid.AddGroup;
import cn.jxj4869.demo.valid.UpdateGroup;
import lombok.Data;


import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;


@Data
public class User {

 @Null(groups = {AddGroup.class})
 @NotNull(groups = {UpdateGroup.class})
 private Integer id;
 
 
 @NotBlank(message = "用戶名不能為空",groups = {AddGroup.class,UpdateGroup.class})
 private String username;
 @NotEmpty
 private String password;
 private String email;
}

在controller中用@Validated注解,指定校驗的分組

 @PostMapping("/user3")
 @ResponseBody
 public R user3(@Validated(UpdateGroup.class) User user) {
  System.out.println(user);

  return null;
 }

結果如下圖所示,因為password屬性沒有指定校驗的分組,所以在校驗的時候,都不會對它進行合法性檢查。

如何在SpringBoot中使用JSR303對后端數據進行校驗

如何在SpringBoot中使用JSR303對后端數據進行校驗

自定義校驗

當提供的注解不能滿足我們需求的時候,可以自定義注解。

例如我們現在給user新加一個屬性status,并要求這個屬性的值只能是0或者1。

新建一個@StatusValue注解。

根據jsr303的規范,校驗注解得有三個屬性。

  • message:用來獲取錯誤提示的

  • groups:指定校驗分組的。

  • payload:可以自定義一些負載信息

使用@Constraint注解指定該注解的校驗器

package cn.jxj4869.demo.valid;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = { StatusValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@interface StatusValue {
 String message() default "{cn.jxj4869.valid.StatusValue.message}";

 Class<?>[] groups() default { };

 Class<? extends Payload>[] payload() default { };

 int[] value() default { };
}

自定義校驗器

需要實現ConstraintValidator這個接口,第一個泛型是表示要校驗哪個注解,第二個泛型是要校驗的數據的類型。

  • initialize是初始化方法

  • isValid校驗方法,判斷是否校驗成功

package cn.jxj4869.demo.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class StatusValueConstraintValidator implements ConstraintValidator<StatusValue,Integer> {

 private Set<Integer> set = new HashSet<>();
 //初始化方法
 @Override
 public void initialize(StatusValue constraintAnnotation) {

  int[] value = constraintAnnotation.value();
  for (int val : value) {
   set.add(val);
  }

 }
 /**
  * 判斷是否校驗成功
  * @param value
  * @param context
  * @return
  */
 @Override
 public boolean isValid(Integer value, ConstraintValidatorContext context) {

  return set.contains(value);
 }
}

最后在resources目錄下添加一個ValidationMessages.properties文件

用來指定錯誤信息。

cn.jxj4869.valid.StatusValue.message=必須提交指定的值

UserBean

@Data
public class User {

 @Null(groups = {AddGroup.class})
 @NotNull(groups = {UpdateGroup.class})
 private Integer id;


 @NotBlank(message = "用戶名不能為空",groups = {AddGroup.class,UpdateGroup.class})
 private String username;
 @NotEmpty
 private String password;
 private String email;

 @StatusValue(value = {0,1},groups = {AddGroup.class,UpdateGroup.class})
 private Integer status;
}

如何在SpringBoot中使用JSR303對后端數據進行校驗

如何在SpringBoot中使用JSR303對后端數據進行校驗

常用注解匯總

注解功能
@Null對象必須為null
@NotNull對象必須不為null,無法檢查長度為0的字符串
@NotBlank字符串必須不為Null,且去掉前后空格長度必須大于0
@NotEmpty字符串必須非空
@Length(min = 1,max = 50)字符串必須在指定長度內
@Range(min = 0,max = 100)必須在指定范圍內
@AssertTrue對象必須為true
@AssertFalse對象必須為false
@Max(Value)必須為數字,且小于或等于Value
@Min(Value)必須為數字,且大于或等于Value
@DecimalMax(Value)必須為數字( BigDecimal ),且小于或等于Value。小數存在精度
@DecimalMin(Value)必須為數字( BigDecimal ),且大于或等于Value。小數存在精度
@Digits(integer,fraction)必須為數字( BigDecimal ),integer整數精度,fraction小數精度
@Size(min,max)對象(Array、Collection、Map、String)長度必須在給定范圍
@Email字符串必須是合法郵件地址
@PastDate和Calendar對象必須在當前時間之前
@FutureDate和Calendar對象必須在當前時間之后
@Pattern(regexp=“正則”)字符串滿足正則表達式的值

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

科技| 仪陇县| 岳西县| 当阳市| 玉溪市| 泽普县| 镇雄县| 湘阴县| 饶河县| 武功县| 乌拉特前旗| 高淳县| 房产| 双峰县| 汉中市| 安吉县| 米脂县| 莆田市| 抚州市| 丘北县| 祁门县| 盐源县| 凤城市| 沁源县| 宁远县| 海兴县| 晋江市| 无棣县| 资阳市| 万全县| 彩票| 大英县| 湖南省| 寿光市| 嵊泗县| 运城市| 华宁县| 庆元县| 聂拉木县| 常宁市| 沿河|