<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      補習系列(4)-springboot 參數校驗詳解

      目標

      1. 對于幾種常見的入參方式,了解如何進行校驗以及該如何處理錯誤消息;
      2. 了解springboot 內置的參數異常類型,并能利用攔截器實現自定義處理;
      3. 能實現簡單的自定義校驗規則

      一、PathVariable 校驗

      在定義 Restful 風格的接口時,通常會采用 PathVariable 指定關鍵業務參數,如下:

      @GetMapping("/path/{group:[a-zA-Z0-9_]+}/{userid}")
      @ResponseBody
      public String path(@PathVariable("group") String group, @PathVariable("userid") Integer userid) {
          return group + ":" + userid;
      }
      

      {group:[a-zA-Z0-9_]+} 這樣的表達式指定了 group 必須是以大小寫字母、數字或下劃線組成的字符串。
      我們試著訪問一個錯誤的路徑:

      GET /path/testIllegal.get/10000
      

      此時會得到 404的響應,因此對于PathVariable 僅由正則表達式可達到校驗的目的

      二、方法參數校驗

      類似前面的例子,大多數情況下,我們都會直接將HTTP請求參數映射到方法參數上。

      @GetMapping("/param")
      @ResponseBody
      public String param(@RequestParam("group")@Email String group, 
                          @RequestParam("userid") Integer userid) {
         return group + ":" + userid;
      }
      

      上面的代碼中,@RequestParam 聲明了映射,此外我們還為 group 定義了一個規則(復合Email格式)
      這段代碼是否能直接使用呢?答案是否定的,為了啟用方法參數的校驗能力,還需要完成以下步驟:

      • 聲明 MethodValidationPostProcessor
      @Bean
      public MethodValidationPostProcessor methodValidationPostProcessor() {
           return new MethodValidationPostProcessor();
      }
      
      • Controller指定@Validated注解
      @Controller
      @RequestMapping("/validate")
      @Validated
      public class ValidateController {
      

      如此之后,方法上的@Email規則才能生效。

      校驗異常
      如果此時我們嘗試通過非法參數進行訪問時,比如提供非Email格式的 group
      會得到以下錯誤:

      GET /validate/param?group=simple&userid=10000
      ====>
      {
          "timestamp": 1530955093583,
          "status": 500,
          "error": "Internal Server Error",
          "exception": "javax.validation.ConstraintViolationException",
          "message": "No message available",
          "path": "/validate/param"
      }
      

      而如果參數類型錯誤,比如提供非整數的 userid,會得到:

      GET /validate/param?group=simple&userid=1f
      ====>
      {
          "timestamp": 1530954430720,
          "status": 400,
          "error": "Bad Request",
          "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
          "message": "Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: \"1f\"",
          "path": "/validate/param"
      }
      

      當存在參數缺失時,由于定義的@RequestParam注解中,屬性 required=true,也將會導致失敗:

      GET /validate/param?userid=10000
      ====>
      {
          "timestamp": 1530954345877,
          "status": 400,
          "error": "Bad Request",
          "exception": "org.springframework.web.bind.MissingServletRequestParameterException",
          "message": "Required String parameter 'group' is not present",
          "path": "/validate/param"
      }
      

      三、表單對象校驗

      頁面的表單通常比較復雜,此時可以將請求參數封裝到表單對象中,
      并指定一系列對應的規則,參考JSR-303

      public static class FormRequest {
      
          @NotEmpty
          @Email
          private String email;
      
          @Pattern(regexp = "[a-zA-Z0-9_]{6,30}")
          private String name;
      
          @Min(5)
          @Max(199)
          private int age;
      

      上面定義的屬性中:

      • email必須非空、符合Email格式規則;
      • name必須為大小寫字母、數字及下劃線組成,長度在6-30個;
      • age必須在5-199范圍內

      Controller方法中的定義:

      @PostMapping("/form")
      @ResponseBody
      public FormRequest form(@Validated FormRequest form) {
          return form;
      }
      

      @Validated指定了參數對象需要執行一系列校驗。

      校驗異常
      此時我們嘗試構造一些違反規則的輸入,會得到以下的結果:

      {
          "timestamp": 1530955713166,
          "status": 400,
          "error": "Bad Request",
          "exception": "org.springframework.validation.BindException",
          "errors": [
              {
                  "codes": [
                      "Email.formRequest.email",
                      "Email.email",
                      "Email.java.lang.String",
                      "Email"
                  ],
                  "arguments": [
                      {
                          "codes": [
                              "formRequest.email",
                              "email"
                          ],
                          "arguments": null,
                          "defaultMessage": "email",
                          "code": "email"
                      },
                      [],
                      {
                          "arguments": null,
                          "codes": [
                              ".*"
                          ],
                          "defaultMessage": ".*"
                      }
                  ],
                  "defaultMessage": "不是一個合法的電子郵件地址",
                  "objectName": "formRequest",
                  "field": "email",
                  "rejectedValue": "tecom",
                  "bindingFailure": false,
                  "code": "Email"
              },
              {
                  "codes": [
                      "Pattern.formRequest.name",
                      "Pattern.name",
                      "Pattern.java.lang.String",
                      "Pattern"
                  ],
                  "arguments": [
                      {
                          "codes": [
                              "formRequest.name",
                              "name"
                          ],
                          "arguments": null,
                          "defaultMessage": "name",
                          "code": "name"
                      },
                      [],
                      {
                          "arguments": null,
                          "codes": [
                              "[a-zA-Z0-9_]{6,30}"
                          ],
                          "defaultMessage": "[a-zA-Z0-9_]{6,30}"
                      }
                  ],
                  "defaultMessage": "需要匹配正則表達式\"[a-zA-Z0-9_]{6,30}\"",
                  "objectName": "formRequest",
                  "field": "name",
                  "rejectedValue": "fefe",
                  "bindingFailure": false,
                  "code": "Pattern"
              },
              {
                  "codes": [
                      "Min.formRequest.age",
                      "Min.age",
                      "Min.int",
                      "Min"
                  ],
                  "arguments": [
                      {
                          "codes": [
                              "formRequest.age",
                              "age"
                          ],
                          "arguments": null,
                          "defaultMessage": "age",
                          "code": "age"
                      },
                      5
                  ],
                  "defaultMessage": "最小不能小于5",
                  "objectName": "formRequest",
                  "field": "age",
                  "rejectedValue": 2,
                  "bindingFailure": false,
                  "code": "Min"
              }
          ],
          "message": "Validation failed for object='formRequest'. Error count: 3",
          "path": "/validate/form"
      }
      
      

      如果是參數類型不匹配,會得到:

      {
          "timestamp": 1530955359265,
          "status": 400,
          "error": "Bad Request",
          "exception": "org.springframework.validation.BindException",
          "errors": [
              {
                  "codes": [
                      "typeMismatch.formRequest.age",
                      "typeMismatch.age",
                      "typeMismatch.int",
                      "typeMismatch"
                  ],
                  "arguments": [
                      {
                          "codes": [
                              "formRequest.age",
                              "age"
                          ],
                          "arguments": null,
                          "defaultMessage": "age",
                          "code": "age"
                      }
                  ],
                  "defaultMessage": "Failed to convert property value of type 'java.lang.String' 
      to required type 'int' for property 'age'; nested exception is java.lang.NumberFormatException: 
      For input string: \"\"",
                  "objectName": "formRequest",
                  "field": "age",
                  "rejectedValue": "",
                  "bindingFailure": true,
                  "code": "typeMismatch"
              }
          ],
          "message": "Validation failed for object='formRequest'. Error count: 1",
          "path": "/validate/form"
      }
      

      Form表單參數上,使用@Valid注解可達到同樣目的,而關于兩者的區別則是:

      @Valid 基于JSR303,即 Bean Validation 1.0,由Hibernate Validator實現;
      @Validated 基于JSR349,是Bean Validation 1.1,由Spring框架擴展實現;

      后者做了一些增強擴展,如支持分組校驗,有興趣可參考這里

      四、RequestBody 校驗

      對于直接Json消息體輸入,同樣可以定義校驗規則:

      @PostMapping("/json")
      @ResponseBody
      public JsonRequest json(@Validated @RequestBody JsonRequest request) {
      
          return request;
      }
      
      ...
      public static class JsonRequest {
      
          @NotEmpty
          @Email
          private String email;
      
          @Pattern(regexp = "[a-zA-Z0-9_]{6,30}")
          private String name;
      
          @Min(5)
          @Max(199)
          private int age;
      

      校驗異常
      構造一個違反規則的Json請求體進行輸入,會得到:

      {
          "timestamp": 1530956161314,
          "status": 400,
          "error": "Bad Request",
          "exception": "org.springframework.web.bind.MethodArgumentNotValidException",
          "errors": [
              {
                  "codes": [
                      "Min.jsonRequest.age",
                      "Min.age",
                      "Min.int",
                      "Min"
                  ],
                  "arguments": [
                      {
                          "codes": [
                              "jsonRequest.age",
                              "age"
                          ],
                          "arguments": null,
                          "defaultMessage": "age",
                          "code": "age"
                      },
                      5
                  ],
                  "defaultMessage": "最小不能小于5",
                  "objectName": "jsonRequest",
                  "field": "age",
                  "rejectedValue": 1,
                  "bindingFailure": false,
                  "code": "Min"
              }
          ],
          "message": "Validation failed for object='jsonRequest'. Error count: 1",
          "path": "/validate/json"
      }
      
      

      此時與FormBinding的情況不同,我們得到了一個MethodArgumentNotValidException異常。
      而如果發生參數類型不匹配,比如輸入age=1f,會產生以下結果:

      {
          "timestamp": 1530956206264,
          "status": 400,
          "error": "Bad Request",
          "exception": "org.springframework.http.converter.HttpMessageNotReadableException",
          "message": "Could not read document: Can not deserialize value of type int from String \"ff\": not a valid Integer value\n at [Source: java.io.PushbackInputStream@68dc9800; line: 2, column: 8] (through reference chain: org.zales.dmo.boot.controllers.ValidateController$JsonRequest[\"age\"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type int from String \"ff\": not a valid Integer value\n at [Source: java.io.PushbackInputStream@68dc9800; line: 2, column: 8] (through reference chain: org.zales.dmo.boot.controllers.ValidateController$JsonRequest[\"age\"])",
          "path": "/validate/json"
      }
      

      這表明在JSON轉換過程中已經失敗!

      五、自定義校驗規則

      框架內預置的校驗規則可以滿足大多數場景使用,
      但某些特殊情況下,你需要制作自己的校驗規則,這需要用到ContraintValidator接口。

      我們以一個密碼校驗的場景作為示例,比如一個注冊表單上,
      我們需要檢查 密碼輸入密碼確認 是一致的。

      **首先定義 PasswordEquals 注解

      @Documented
      @Constraint(validatedBy = { PasswordEqualsValidator.class })
      @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
      @Retention(RetentionPolicy.RUNTIME)
      public @interface PasswordEquals {
      
          String message() default "Password is not the same";
      
          Class<?>[] groups() default {};
      
          Class<? extends Payload>[] payload() default {};
      }
      

      在表單上聲明@PasswordEquals 注解

      @PasswordEquals
      public class RegisterForm {
         
          @NotEmpty
          @Length(min=5,max=30)
          private String username;
          
          @NotEmpty
          private String password;
          
          @NotEmpty
          private String passwordConfirm;
      

      針對@PasswordEquals實現校驗邏輯

      public class PasswordEqualsValidator implements ConstraintValidator<PasswordEquals, RegisterForm> {
      
          @Override
          public void initialize(PasswordEquals anno) {
          }
      
          @Override
          public boolean isValid(RegisterForm form, ConstraintValidatorContext context) {
              String passwordConfirm = form.getPasswordConfirm();
              String password = form.getPassword();
      
              boolean match = passwordConfirm != null ? passwordConfirm.equals(password) : false;
              if (match) {
                  return true;
              }
      
              String messageTemplate = context.getDefaultConstraintMessageTemplate();
              
              // disable default violation rule
              context.disableDefaultConstraintViolation();
      
              // assign error on password Confirm field
              context.buildConstraintViolationWithTemplate(messageTemplate).addPropertyNode("passwordConfirm")
                      .addConstraintViolation();
              return false;
      
          }
      }
      
      

      如此,我們已經完成了自定義的校驗工作。

      六、異常攔截器

      SpringBoot 框架中可通過 @ControllerAdvice 實現Controller方法的攔截操作。
      可以利用攔截能力實現一些公共的功能,比如權限檢查、頁面數據填充,以及全局的異常處理等等。

      在前面的篇幅中,我們提及了各種校驗失敗所產生的異常,整理如下表:

      異常類型 描述
      ConstraintViolationException 違反約束,javax擴展定義
      BindException 綁定失敗,如表單對象參數違反約束
      MethodArgumentNotValidException 參數無效,如JSON請求參數違反約束
      MissingServletRequestParameterException 參數缺失
      TypeMismatchException 參數類型不匹配

      如果希望對這些異常實現統一的捕獲,并返回自定義的消息,
      可以參考以下的代碼片段:

      @ControllerAdvice
      public static class CustomExceptionHandler extends ResponseEntityExceptionHandler {
      
          @ExceptionHandler(value = { ConstraintViolationException.class })
          public ResponseEntity<String> handle(ConstraintViolationException e) {
              Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
              StringBuilder strBuilder = new StringBuilder();
              for (ConstraintViolation<?> violation : violations) {
                  strBuilder.append(violation.getInvalidValue() + " " + violation.getMessage() + "\n");
              }
              String result = strBuilder.toString();
              return new ResponseEntity<String>("ConstraintViolation:" + result, HttpStatus.BAD_REQUEST);
          }
      
          @Override
          protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,
                  WebRequest request) {
              return new ResponseEntity<Object>("BindException:" + buildMessages(ex.getBindingResult()),
                      HttpStatus.BAD_REQUEST);
          }
      
          @Override
          protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                  HttpHeaders headers, HttpStatus status, WebRequest request) {
              return new ResponseEntity<Object>("MethodArgumentNotValid:" + buildMessages(ex.getBindingResult()),
                      HttpStatus.BAD_REQUEST);
          }
      
          @Override
          public ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
                  HttpHeaders headers, HttpStatus status, WebRequest request) {
              return new ResponseEntity<Object>("ParamMissing:" + ex.getMessage(), HttpStatus.BAD_REQUEST);
          }
      
          @Override
          protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers,
                  HttpStatus status, WebRequest request) {
              return new ResponseEntity<Object>("TypeMissMatch:" + ex.getMessage(), HttpStatus.BAD_REQUEST);
          }
      
          private String buildMessages(BindingResult result) {
              StringBuilder resultBuilder = new StringBuilder();
      
              List<ObjectError> errors = result.getAllErrors();
              if (errors != null && errors.size() > 0) {
                  for (ObjectError error : errors) {
                      if (error instanceof FieldError) {
                          FieldError fieldError = (FieldError) error;
                          String fieldName = fieldError.getField();
                          String fieldErrMsg = fieldError.getDefaultMessage();
                          resultBuilder.append(fieldName).append(" ").append(fieldErrMsg).append(";");
                      }
                  }
              }
              return resultBuilder.toString();
          }
      }
      

      默認情況下,對于非法的參數輸入,框架會產生 **HTTP_BAD_REQUEST(status=400) ** 錯誤碼,
      并輸出友好的提示消息,這對于一般情況來說已經足夠。

      更多的輸入校驗及提示功能應該通過客戶端去完成(服務端僅做同步檢查),
      客戶端校驗的用戶體驗更好,而這也符合富客戶端(rich client)的發展趨勢。

      碼云同步代碼

      參考文檔

      springmvc-validation樣例
      使用validation api進行操作
      hibernate-validation官方文檔
      Bean-Validation規范

      歡迎繼續關注"美碼師的補習系列-springboot篇" ,期待更多精彩內容-

      posted @ 2018-07-30 17:44  美碼師  閱讀(9774)  評論(3)    收藏  舉報
      主站蜘蛛池模板: 亚洲欧美在线观看| 尹人香蕉久久99天天拍欧美p7| 久久久久久毛片免费播放| 丁香五月天综合缴情网| 欧美 变态 另类 人妖| 九九热精品在线观看| 日韩精品 在线 国产 丝袜| 亚洲色婷婷综合开心网| 国产v亚洲v天堂无码久久久| 亚洲日本欧美日韩中文字幕| 弥勒县| 国产鲁鲁视频在线观看| 色爱区综合激情五月激情| 青青草无码免费一二三区| 午夜福利精品国产二区| 热久在线免费观看视频| 人人妻人人澡人人爽| 高清无码18| 国产午夜一区二区在线观看| 激情五月日韩中文字幕| 国内不卡不区二区三区| 四川丰满少妇无套内谢| 成人精品国产一区二区网| 日韩精品一区二区三区激情视频| 伊人久久大香线蕉av五月天| 亚洲国产精品午夜福利| 极品蜜臀黄色在线观看| 亚洲中文字幕成人无码| 亚洲综合网国产精品一区| 日本久久高清一区二区三区毛片| 狠狠干| 狠狠色噜噜狠狠狠狠7777米奇| 偷拍专区一区二区三区| jizz国产免费观看| 色噜噜一区二区三区| 日本边添边摸边做边爱喷水| 国产大学生自拍三级视频| 给我免费观看片在线| 国产午夜亚洲精品一区| 国产精品白丝久久AV网站| 国产睡熟迷奷系列网站|