1はじめに
パラメータ検証とは何ですか?
プロジェクトを書くとき、コントローラーを書くとき、私たちは多かれ少なかれこのようなコードを書きました
@RequestMapping("/{studentId}")
public ResponseEntity<String> queryInfo(@PathVariable("studentId") String sudentId){
if(!StringUtils.isEmpty(sudentId)){
//....
}
}
フロントエンドから送信されたデータを検証してから、業務を
遂行する必要があります...検証を検証するだけだと思います。しかし、検証する必要のあるデータが多い場合、1つのメソッドに10のメソッドがあり、このように100のメソッドがあります...データの検証に時間を浪費することになります。明らかにこれは望ましくありません。
コントローラ層では、インターフェイスの入力パラメータを検証する必要がある場合があります。独自の検証ロジックコードを使用して実装すると、いくつかの欠点があります。まず、気が散ってビジネスの記述に集中できなくなります。論理コード; 2つ目は、検証論理コードとビジネス論理コードを結合することであり、コードサイズは比較的肥大化しています。この状況を回避するために、Spring検証のValidatedアノテーションを使用して、インターフェースパラメーターの検証作業を完了することができます。次の例を示します。
2.使用方法
2.1Mavenの依存関係を追加する
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
2.2検証フィールドに検証メモを追加する
class Profile{
@NotNull(message = "字段值不能为空")
private String name;
@NotNull
private String sex;
@Max(value = 20,message = "最大长度为20")
private String address;
@NotNull
@Size(max=10,min=5,message = "字段长度要在5-10之间")
private String fileName;
@Pattern(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$",message = "不满足邮箱正则表达式")
private String email;
@AssertTrue(message = "字段为true才能通过")
private boolean isSave;
@Future(message = "时间在当前时间之后才可以通过")
private Date date;
}
2.3コントローラーレイヤーでの検証に@Validatedを使用する
@RequestMapping("file/upload")
public void upload(@RequestPart("files") MultipartFile files, @Validated Profile profile, Errors error) throws IOException {
if(error.hasErrors()){
return;
}
files.transferTo(new File(files.getOriginalFilename()));保存文件
}
コントローラで直接確認することもできます
- コントローラが検証済みの検証をオンにします
@RestController
@RequestMapping("/test")
@Validated
public class TestController{
...}
- メソッドで直接パラメータを検証します
@GetMapping(value = "/method/{type}")
public ResponseModel test(
@Pattern(regexp = "[1,2]",message = "类型只能为1或2")@PathVariable(value = "type")String types,
@NotBlank(message="内容不能为空")String content,
@Length(max = 64,message = "长度最大为64")String title,
@NotEmpty(message = "集合不能为空")List<String> ids,
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$",message = "手机号格式有误") @NotBlank String mobile,
@Max(value = 100,message = "最大值为100") @Min(value = 1,message = "最小值为1")Integer intValue) {
...}
2.4注釈とは何ですか
@Null注釈付き要素はnullである必要があります
@NotNull注釈付き要素はnullである必要があります
@AssertTrue注釈付き要素はtrueである必要があります
@AssertFalse注釈付き要素はfalseである必要があります
@Min(value)注釈付き要素は数値である必要があります。指定された最小値
以上@Max(value)注釈付き要素は数値である必要があり、その値は指定された最大値以下である必要があります
@DecimalMin(value)注釈付き要素は数値である必要があります。その値は指定された最小値以上である必要があります
@DecimalMax(value)注釈付き要素は数値である必要があり、その値は指定された最大値以下である必要があります
@Size(max =、min =)サイズ注釈付き要素のは指定された範囲内である必要があります
@Digits(整数、分数)注釈付き要素は数値であり、その値は許容範囲内である必要があります
@Past注釈付き要素は過去の日付である必要があります
@Future注釈付き要素は将来の日付である必要があります
@Pattern(regex =、flag =)コメント化された要素は、指定された正規式に準拠している必要があります
2.5グループチェック
このシナリオを紹介します。ユーザーのアカウントパスワードの編集と保存、およびパスワードの追加には、2つの異なる状況があります。
編集と変更->保存:ユーザー名とパスワードが条件を満たしているかどうかを確認するだけで、IDを確認する必要はありません(IDはデータベースにすでに存在するため)。
新規->保存:ユーザー名とパスワードが条件を満たしているかどうかを確認し、IDを確認する必要性を追加しました。
現時点では、グループを使用してBean属性変数を検証します。これにより、複数の検証も満たすことができます。具体的には、次の2つの手順が必要です
手順1:グループインターフェイスクラスを作成する
パケットインターフェイスクラスは単なる通常のインターフェイスクラスであり、あまり意味がありません。この属性が検証される状況を識別するためにのみ使用されます。これは、java.io.Serializable
public interface addUser{
}
public interface editUser{
}
手順2:xxx.classインターフェイスをコントローラーメソッドパラメーターに追加する
新しく追加されたユーザーに対してID検証を実行する@Validated({addUser.class})
と、新しく追加されたユーザーUser.getId()
を検証する必要があることを示すインターフェイスクラスが追加されます。
@Controller
public class UserController {
@RequestMapping("/saveAdd")
public String saveAddUser(@Validated({
addUser.class}) User user, BindingResult result) {
if(result.hasErrors()) {
return "error";
}
return "success";
}
}
ステップ3:Beanにグループを追加する
Userエンティティクラスにグループ化されたグループを追加する@NotEmpty(groups={addUser.class})
ことはUserController@Validated({addUser.class})
に対応し、saveAddUser
新しく追加されたユーザーIDは、新しく追加されたユーザーが実行されたときにのみ検証されることを示します。
public class User {
//在分组addUser时,验证id不能为空,其他情况下不做验证
@NotEmpty(groups={
addUser.class})
private String id;
@NotEmpty(message = "用户名不能为空")
private String username;
@Size(min=6 ,max= 20 ,message = "密码最少6位,最高20位")
private String password;
......
}
上記の3つの手順は、グループ検証を簡単に完了することができますが、グループ検証に3つのポイントを追加します。
- グループが割り当てられていない場合、デフォルトでは毎回認証が必要です
- 次のコードに示すように、グループのグループ化を通じて、同じ変数に対して複数の検証を実行できます。
//对用户名进行两次不同情况的验证。
@NotEmpty(groups={
First.class})
@Size(min=1,max=10,groups={
Second.class})
public String username;
- デフォルトでは、異なるグループ化制約が順不同で検証されますが、検証の相互制約が非常に重要な場合があります(たとえば、前のグループ検証が失敗した、後続の検証が検証されないなど)。グループのグループ化には、前後の検証順序もあります。@GroupSequenceアノテーションを使用して並べ替えます。
/*
* 分组顺序接口类
*/
import javax.validation.GroupSequence;
//分组序列先Frist再Second
@GroupSequence({
First.class,Second.class})
public interface Group{
}
@Controller
public class UserController {
@RequestMapping("/saveAdd")
public String saveAddUser(@Validated({
Group.class}) User user, BindingResult result) {
if(result.hasErrors()) {
return "error";
}
return "success";
}
3.例外キャッチ
上記でデータ検証の実行方法を学びましたが、問題があります。検証に失敗した場合、例外が発生した場合はどうすればよいですか?例外情報を取得してから、発生した例外をフロントエンドに通知します。
それをJSONオブジェクトにカプセル化してフロントエンドに返す方法は?ケースを使用してデモンストレーションする
- テストBeanを追加し、テストクラスにいくつかのフィールドを定義します。各フィールドには、特定の制限があります。
public class BeanValidation {
@Size(min=6,max=10)
private String field1;
@Range(min=0,max=1)
private Long field2;
@AssertFalse
private Boolean field3;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public Long getField2() {
return field2;
}
public void setField2(Long field2) {
this.field2 = field2;
}
public Boolean getField3() {
return field3;
}
public void setField3(Boolean field3) {
this.field3 = field3;
}
}
- テストインターフェイスを追加します。@ Validatedアノテーションは、パラメータの有効性を確認するためにインターフェイスで使用されます。パラメータが有効な場合は、元のデータを返します。
@RequestMapping("globalexceptiontest")
public Object globalExceptionTest(@Validated @RequestBody BeanValidation data)
{
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), data);
return resultMsg;
}
- グローバル例外処理が追加されていない場合、以下に示すように、デフォルトの例外処理が使用され、結果が返されます。
返される結果は、それ自体のデータ構造とは大きく異なり、フロントエンド処理が結果を返すのも非常に面倒です。
- カスタムパラメータ例外によって返されるデータクラス
ArgumentInvalidResult
public class ArgumentInvalidResult {
private String field;
private Object rejectedValue;
private String defaultMessage;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Object getRejectedValue() {
return rejectedValue;
}
public void setRejectedValue(Object rejectedValue) {
this.rejectedValue = rejectedValue;
}
public String getDefaultMessage() {
return defaultMessage;
}
public void setDefaultMessage(String defaultMessage) {
this.defaultMessage = defaultMessage;
}
}
- グローバル例外処理クラスGlobalExceptionHandlerを追加します
@ControllerAdvice
//如果返回的为json数据或其它对象,添加该注解
@ResponseBody
public class GlobalExceptionHandler {
//添加全局异常处理流程,根据需要设置需要处理的异常,本文以MethodArgumentNotValidException为例
@ExceptionHandler(value=MethodArgumentNotValidException.class)
public Object MethodArgumentNotValidHandler(HttpServletRequest request,
MethodArgumentNotValidException exception) throws Exception
{
//按需重新封装需要返回的错误信息
List<ArgumentInvalidResult> invalidArguments = new ArrayList<>();
//解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
for (FieldError error : exception.getBindingResult().getFieldErrors()) {
ArgumentInvalidResult invalidArgument = new ArgumentInvalidResult();
invalidArgument.setDefaultMessage(error.getDefaultMessage());
invalidArgument.setField(error.getField());
invalidArgument.setRejectedValue(error.getRejectedValue());
invalidArguments.add(invalidArgument);
}
ResultMsg resultMsg = new ResultMsg(ResultStatusCode.PARAMETER_ERROR.getErrcode(), ResultStatusCode.PARAMETER_ERROR.getErrmsg(), invalidArguments);
return resultMsg;
}
}
- テストを実行する
- パラメータが有効な場合
- パラメータが不正な場合は、パラメータが有効な場合と同じスタイルのエラーメッセージを返します
注意:
- ここで、@ ControllerAdviceに注釈が付けられ、@ ControllerAdviceは@Controllerの拡張バージョンであり、通常は@ExceptionHandlerと組み合わせて使用されます。
@Controllerにアノテーションが付けられている場合、例外処理は現在のコントローラークラスのメソッドでのみ機能しますが、@ ControllerAdviceを使用すると、グローバルに機能します。
- キャプチャする例外クラスのクラスオブジェクトを@ExceptionHandlerアノテーションに入力します