日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

今天繼續為大家分享在工作中如何優雅的校驗接口的參數的合法性以及如何統一處理接口返回的json格式。每個字都是干貨,原創不易,分享不易。

Spring Boot 優雅地實現接口參數校驗

 

validation主要是校驗用戶提交的數據的合法性,比如是否為空,密碼是否符合規則,郵箱格式是否正確等等,校驗框架比較多,用的比較多的是hibernate-validator, 也支持國際化,也可以自定義校驗類型的注解,這里只是簡單地演示校驗框架在Spring Boot中的簡單集成,要想了解更多可以參考 hibernate-validator。

1. pom.xml

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

2. dto

public class UserInfoIDto {

    private Long id;

    @NotBlank
    @Length(min=3, max=10)
    private String username;

    @NotBlank
    @Email
    private String email;

    @NotBlank
    @Pattern(regexp="^((13[0-9])|(15[^4,\D])|(18[0,3-9]))\d{8}$", message="手機號格式不正確")
    private String phone;

    @Min(value=18)
    @Max(value = 200)
    private int age;

    @NotBlank
    @Length(min=6, max=12, message="昵稱長度為6到12位")
    private String nickname;

     // Getter & Setter
}

3. controller

import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

@RestController
public class SimpleController {

    @PostMApping("/users")
    public String register(@Valid @RequestBody UserInfoIDto userInfoIDto, BindingResult result){
        if (result.hasErrors()) {
            FieldError fieldError = result.getFieldError();
            String field = fieldError.getField();
            String msg = fieldError.getDefaultMessage();

            return field + ":" + msg;
        }
        System.out.println("開始注冊用戶...");

        return "success";
    }
}
Spring Boot 優雅地實現接口參數校驗

 

4. 去掉BindingResult參數

每個接口都需要BindingResult參數,而且每個接口都需要處理錯誤信息,這樣增加一個參數也不優雅,處理錯誤信息代碼量也很重復。如果去掉BindingResult參數,系統就會報錯MethodArgumentNotValidException,我們只需要使用全局異常來捕獲該錯誤,處理一下就可以省略傳BindingResult參數了。

@RestController
public class SimpleController {

    @PostMapping("/users")
    public String register(@Valid @RequestBody UserInfoIDto userInfoIDto){
        System.out.println("開始注冊用戶...");
        return "success";
    }
}

@RestControllerAdvice 用于攔截所有的@RestController

@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String methodArgumentNotValidException(MethodArgumentNotValidException e) {
        // 從異常對象中拿到ObjectError對象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取錯誤提示信息進行返回
        return objectError.getDefaultMessage();
    }
}
Spring Boot 優雅地實現接口參數校驗

 

5. 統一返回格式

錯誤碼枚舉

@Getter
public enum ErrorCodeEnum {
    SUCCESS(1000, "成功"),
    FAILED(1001, "響應失敗"),
    VALIDATE_FAILED(1002, "參數校驗失敗"),
    ERROR(5000, "未知錯誤");

    private Integer code;
    private String msg;

    ErrorCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

自定義異常。

@Getter
public class APIException extends RuntimeException {
    private int code;
    private String msg;

    public APIException(ErrorCodeEnum errorCodeEnum) {
        super(errorCodeEnum.getMsg());
        this.code = errorCodeEnum.getCode();
        this.msg = errorCodeEnum.getMsg();
    }
}

定義返回格式。

@Getter
public class Response<T> {
    /**
     * 狀態碼,比如1000代表響應成功
     */
    private int code;

    /**
     * 響應信息,用來說明響應情況
     */
    private String msg;

    /**
     * 響應的具體數據
     */
    private T data;


    public Response(T data) {
        this.code = ErrorCodeEnum.SUCCESS.getCode();
        this.msg = ErrorCodeEnum.SUCCESS.getMsg();
        this.data = data;
    }

    public Response(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

全局異常處理器增加對APIException的攔截,并修改異常時返回的數據格式。

@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response<String> methodArgumentNotValidException(MethodArgumentNotValidException e) {
        // 從異常對象中拿到ObjectError對象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取錯誤提示信息進行返回
        return new Response<>(ErrorCodeEnum.VALIDATE_FAILED.getCode(), objectError.getDefaultMessage());
    }


    @ExceptionHandler(APIException.class)
    public Response<String> APIExceptionHandler(APIException e) {
        return new Response<>(e.getCode(), e.getMsg());
    }
}

SimpleController 增加一個拋出異常的方法。

@RestController
public class SimpleController {

    @PostMapping("/users")
    public String register(@Valid @RequestBody UserInfoIDto userInfoIDto){
        System.out.println("開始注冊用戶...");
        return "success";
    }

    @GetMapping("/users")
    public Response<UserInfoIDto> list() {
        UserInfoIDto userInfoIDto = new UserInfoIDto();
        userInfoIDto.setUsername("monday");
        userInfoIDto.setAge(30);
        userInfoIDto.setPhone("123456789");
        if (true) {
            throw new APIException(ErrorCodeEnum.ERROR);
        }
        // 為了保持數據格式統一,必須使用Response包裝一下
        return new Response<>(userInfoIDto);
    }
}
Spring Boot 優雅地實現接口參數校驗

 

報錯返回的格式。

Spring Boot 優雅地實現接口參數校驗

 

不報錯,返回的格式。

Spring Boot 優雅地實現接口參數校驗

 

6. 去掉接口中的Response包裝

@RestControllerAdvice既可以全局攔截異常也可攔截指定包下正常的返回值,可以對返回值進行修改。

@RestControllerAdvice(basePackages = {"com.example.validator.controller"})
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {

    /**
     * 對那些方法需要包裝,如果接口直接返回Response就沒有必要再包裝了
     *
     * @param returnType
     * @param aClass
     * @return 如果為true才會執行beforeBodyWrite
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
        return !returnType.getParameterType().equals(Response.class);
    }


    @Override
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
        // String類型不能直接包裝,所以要進行些特別的處理
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                // 將數據包裝在Response里后,再轉換為json字符串響應給前端
                return objectMapper.writeValueAsString(new Response<>(data));
            } catch (JsonProcessingException e) {
                throw new APIException(ErrorCodeEnum.ERROR);
            }
        }
        // 這里統一包裝
        return new Response<>(data);
    }
}
@RestController
public class SimpleController {

    @GetMapping("/users")
    public UserInfoIDto list() {
        UserInfoIDto userInfoIDto = new UserInfoIDto();
        userInfoIDto.setUsername("monday");
        userInfoIDto.setAge(30);
        userInfoIDto.setPhone("123456789");
        // 直接返回值,不需要再使用Response包裝
        return userInfoIDto;
    }
}
Spring Boot 優雅地實現接口參數校驗

 

7. 每個校驗錯誤都對應不同的錯誤碼

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ValidateErrorCode {
    /** 校驗錯誤碼 code */
    int value() default 100000;
}
@Data
public class UserInfoIDto {
    @NotBlank
    @Email
    @ValidateErrorCode(value = 20000)
    private String email;

    @NotBlank
    @Pattern(regexp="^((13[0-9])|(15[^4,\D])|(18[0,3-9]))\d{8}$", message="手機號格式不正確")
    @ValidateErrorCode(value = 30000)
    private String phone;
}

校驗異常獲取注解中的錯誤碼。

@ExceptionHandler(MethodArgumentNotValidException.class)
    public Response<String> methodArgumentNotValidException(MethodArgumentNotValidException e) throws NoSuchFieldException {
        // 從異常對象中拿到ObjectError對象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);

        // 參數的Class對象,等下好通過字段名稱獲取Field對象
        Class<?> parameterType = e.getParameter().getParameterType();
        // 拿到錯誤的字段名稱
        String fieldName = e.getBindingResult().getFieldError().getField();
        Field field = parameterType.getDeclaredField(fieldName);
        // 獲取Field對象上的自定義注解
        ValidateErrorCode annotation = field.getAnnotation(ValidateErrorCode.class);
        if (annotation != null) {
            return new Response<>(annotation.value(),objectError.getDefaultMessage());
        }

        // 然后提取錯誤提示信息進行返回
        return new Response<>(ErrorCodeEnum.VALIDATE_FAILED.getCode(), objectError.getDefaultMessage());
    }
Spring Boot 優雅地實現接口參數校驗

 


Spring Boot 優雅地實現接口參數校驗

 

8. 個別接口不統一包裝響應

有時候第三方接口回調我們的接口,我們的接口必須按照第三方定義的返回格式來,此時第三方不一定和我們自己的返回格式一樣,所以要提供一種可以繞過統一包裝的方式。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface NotResponseWrap {
}
@RestController
public class SimpleController {

    @NotResponseWrap
    @PostMapping("/users")
    public String register(@Valid @RequestBody UserInfoIDto userInfoIDto){
        System.out.println("開始注冊用戶...");
        return "success";
    }
}

ResponseControllerAdvice 增加一個不包裝的條件,配置了@NotResponseWrap注解就跳過包裝。

@Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
        return !(returnType.getParameterType().equals(Response.class) || returnType.hasMethodAnnotation(NotResponseWrap.class));
    }
Spring Boot 優雅地實現接口參數校驗

分享到:
標簽:Spring Boot
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定