一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式

SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式

2022-02-23 00:38shalousun Java教程

這篇文章主要介紹了SpringBoot validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

validator參數(shù)驗(yàn)證restful自定義錯(cuò)誤碼響應(yīng)

關(guān)于spring web應(yīng)用中關(guān)于如何使用 Bean Validation API和hibernate-validator的文章已經(jīng)很多,本文就不再重復(fù)敘述,今天要介紹的重點(diǎn)是在SpringBoot restful服務(wù)中如何根據(jù)不同驗(yàn)證錯(cuò)誤響應(yīng)不同的自定義錯(cuò)誤碼。下面直接上代碼。

一、定義restful統(tǒng)一結(jié)果返回

阿里java開(kāi)發(fā)手冊(cè)中定義的一段參考【“對(duì)于公司外的 http/api 開(kāi)放接口必須使用“錯(cuò)誤碼”; 而應(yīng)用內(nèi)部推薦異常拋出;跨應(yīng)用間 RPC 調(diào)用優(yōu)先考慮使用 Result 方式,封裝 isSuccess()方法、 “錯(cuò)誤碼”、“錯(cuò)誤簡(jiǎn)短信息”。】。因此這里也定義個(gè)返回結(jié)構(gòu)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class CommonResult<T> implements Serializable {
    
    /**
     * serialVersionUID:.
     */
    private static final long serialVersionUID = -7268040542410707954L;
 
    /**
     * 是否成功
     */
    private boolean success = false;
 
    /**
     * 返回信息
     */
    private String message;
 
    /**
     * 裝在數(shù)據(jù)
     */
    private T data;
 
    /**
     * 錯(cuò)誤代碼
     */
    private String code;
 
    /**
     * 默認(rèn)構(gòu)造器
     */
    public CommonResult(){
        
    }
    /**
     *
     * @param success
     *          是否成功
     * @param message
     *          返回的消息
     */
    public CommonResult(boolean success, String message){
        this.success = success;
        this.message = message;
    }
    /**
     *
     * @param success
     *          是否成功
     */
    public CommonResult(boolean success){
        this.success = success;
    }
 
    /**
     *
     * @param code error code
     * @param message success or error messages
     */
    public CommonResult(String code,String message){
        this.code = code;
        this.message = message;
    }
    /**
     *
     * @param success
     *          是否成功
     * @param message
     *          消息
     * @param data
     *          數(shù)據(jù)
     */
    public CommonResult(boolean success, String message, T data){
        this.success = success;
        this.message = message;
        this.data = data;
    }
    //省略get set
}

二、定義一個(gè)錯(cuò)誤碼枚舉

在有需要國(guó)際化的項(xiàng)目,當(dāng)然選擇通過(guò)i18n來(lái)配置更好,此處為了簡(jiǎn)單直接采用枚舉定義。這里定義的錯(cuò)誤僅供參考,不同公司每個(gè)應(yīng)用在實(shí)際情況下可能都不大一樣。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 錯(cuò)誤代碼枚舉類
 *
 */
public enum ErrorCodeEnum {
    SUCCESS("0000", "success"),
    PARAM_EMPTY("1001", "必選參數(shù)為空"),
    PARAM_ERROR("1002", "參數(shù)格式錯(cuò)誤"),
    UNKNOWN_ERROR("9999", "系統(tǒng)繁忙,請(qǐng)稍后再試....");
    private String code;
    private String desc;
    ErrorCodeEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }
 
    public String getCode() {
        return this.code;
    }
 
    public String getDesc() {
        return desc;
    }
 
    @Override
    public String toString() {
        return "[" + this.code + "]" + this.desc;
    }
}

三、靜態(tài)封裝CommonResult

靜態(tài)封裝CommonResult主要是方便在項(xiàng)目中快速根據(jù)邏輯寫(xiě)返回結(jié)果代碼。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * 公共響應(yīng)結(jié)果成功失敗的靜態(tài)方法調(diào)用
 *
 */
public class ResultUtil {
 
 
    /**
     * return success
     *
     * @param data
     * @return
     */
    public static <T> CommonResult<T> returnSuccess(T data) {
        CommonResult<T> result = new CommonResult();
        result.setCode(ErrorCodeEnum.SUCCESS.getCode());
        result.setSuccess(true);
        result.setData(data);
        result.setMessage(ErrorCodeEnum.SUCCESS.getDesc());
        return result;
    }
 
    /**
     * return error
     *
     * @param code error code
     * @param msg  error message
     * @return
     */
    public static CommonResult returnError(String code, String msg) {
        CommonResult result = new CommonResult();
        result.setCode(code);
        result.setData("");
        result.setMessage(msg);
        return result;
    }
 
    /**
     * use enum
     *
     * @param status
     * @return
     */
    public static CommonResult returnError(ErrorCodeEnum status) {
        return returnError(status.getCode(), status.getDesc());
    }
}

四、定義BaseController來(lái)處理驗(yàn)證錯(cuò)誤自定義錯(cuò)誤碼返回

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
 * BaseController
 *
 */
public abstract class BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger(BaseController.class);
    /**
     * validate params
     *
     * @param bindingResult
     * @return
     */
    protected CommonResult validParams(BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            return processBindingError(fieldError);
        }
        return ResultUtil.returnSuccess("");
    }
 
    /**
     * 根據(jù)spring binding 錯(cuò)誤信息自定義返回錯(cuò)誤碼和錯(cuò)誤信息
     *
     * @param fieldError
     * @return
     */
    private CommonResult processBindingError(FieldError fieldError) {
        String code = fieldError.getCode();
        LOGGER.debug("validator error code: {}", code);
        switch (code) {
            case "NotEmpty":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage());
            case "NotBlank":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage());
            case "NotNull":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_EMPTY.getCode(), fieldError.getDefaultMessage());
            case "Pattern":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Min":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Max":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Length":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Range":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Email":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "DecimalMin":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "DecimalMax":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Size":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Digits":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Past":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            case "Future":
                return ResultUtil.returnError(ErrorCodeEnum.PARAM_ERROR.getCode(), fieldError.getDefaultMessage());
            default:
                return ResultUtil.returnError(ErrorCodeEnum.UNKNOWN_ERROR);
        }
    }
}

五、驗(yàn)證實(shí)例

這里直接給出一個(gè)簡(jiǎn)單的參數(shù)驗(yàn)證例子。

Controller繼承上面寫(xiě)的BaseController

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
 * 關(guān)于Validator使用測(cè)試
 *
 */
@RestController
@RequestMapping("validator")
public class ValidatorTestController extends BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ValidatorTestController.class);
    /**
     * validate驗(yàn)證測(cè)試
     *
     * @param leader
     * @param bindingResult
     * @return
     */
    @PostMapping("/test")
    public CommonResult testSimpleValidate(@Valid @RequestBody Leader leader, BindingResult bindingResult) {
        LOGGER.debug("ReqParams:{}", JSON.toJSONString(leader));
        CommonResult result = validParams(bindingResult);
        if (!result.isSuccess()) {
            return result;
        }
        return ResultUtil.returnSuccess("");
    }
}

入?yún)?duì)象Leader代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Leader {
    /**
     * 姓名
     */
    @NotEmpty
    private String name;
 
    /**
     * 生日
     */
    @Pattern(regexp = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", message = "出生日期格式不正確")
    private String birthday;
 
    /**
     * 年齡
     */
    @Min(value = 0)
    private Integer age;
    //省略gettes and  setters
}

這時(shí)項(xiàng)目已經(jīng)已經(jīng)完全可以根據(jù)驗(yàn)證錯(cuò)誤來(lái)返回自定義的錯(cuò)誤碼和提示了。

本例所涉及源代碼:https://github.com/shalousun/api-doc-test

小結(jié)一下

在一些對(duì)外服務(wù)提供restful的應(yīng)用中,根據(jù)不同的驗(yàn)證錯(cuò)誤返回其實(shí)是避免不了。當(dāng)然實(shí)現(xiàn)的方式可以有種,而本文所采用的方式相對(duì)來(lái)說(shuō)簡(jiǎn)單易懂。

使用validator-api驗(yàn)證springboot的參數(shù)

作為服務(wù)端開(kāi)發(fā),驗(yàn)證前端傳入的參數(shù)的合法性是一個(gè)必不可少的步驟,但是驗(yàn)證參數(shù)是一個(gè)基本上是一個(gè)體力活,而且冗余代碼繁多,也影響代碼的可閱讀性,所以有沒(méi)有一個(gè)比較優(yōu)雅的方式來(lái)解決這個(gè)問(wèn)題?

這么簡(jiǎn)單的問(wèn)題當(dāng)然早就有大神遇到并且解決了,這一篇文章主要講一下解決基于spring-boot的驗(yàn)證參數(shù)的比較好的方法:利用validator-api來(lái)進(jìn)行驗(yàn)證參數(shù)。

在spring-boot-starter-web包里面有hibernate-validator包,它提供了一系列驗(yàn)證各種參數(shù)的方法,所以說(shuō)spring-boot已經(jīng)幫我們想好要怎么解決這個(gè)問(wèn)題了。

這篇文章針對(duì)spring-boot里面的spring-mvc介紹三種方式來(lái)驗(yàn)證參數(shù)。

一、這個(gè)方法在網(wǎng)上大部分都可以查到

先假設(shè)我們的restful的接口接受一個(gè)GradeAndClassroomModel類型的對(duì)象,并且這個(gè)類被定義成

?
1
2
3
4
5
6
7
@Data
public class GradeAndClassroomModel { 
@Range(min = 1, max = 9, message = "年級(jí)只能從1-9"
private int grade; 
@Range(min = 1, max = 99, message = "班級(jí)只能從1-99"
private int classroomNumber;
}

利用validator提供的一系列注解,比如本例中的@Range,就可以表示參數(shù)的范圍和出錯(cuò)時(shí)候的提示信息。還有很多其他注解,這里就不一一列出

然后我們的Controller層的代碼為

?
1
2
3
4
5
6
7
8
@RequestMapping(value = "/paramErrorTest", method = RequestMethod.GET)
public String paramErrorTest(   
  @Valid   
  @ModelAttribute   
  GradeAndClassroomModel gradeAndClassroomModel,
  BindingResult result) { 
  return classroomService.getTeacherName(gradeAndClassroomModel.getGrade(), gradeAndClassroomModel.getClassroomNumber());
}

其中如果驗(yàn)證出錯(cuò),result對(duì)象里面就會(huì)有錯(cuò)誤信息,然后可以自己進(jìn)行處理。

二、針對(duì)上面的例子

會(huì)有人說(shuō),就兩個(gè)參數(shù),為什么要作為對(duì)象呢?會(huì)不會(huì)太麻煩?確實(shí),如果只有少數(shù)對(duì)象,直接把參數(shù)寫(xiě)到Controller層,然后在Controller層進(jìn)行驗(yàn)證就可以了。

?
1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "/teacherName", method = RequestMethod.GET)
public String teacherName(
  @Range(min = 1, max = 9, message = "年級(jí)只能從1-9")       
  @RequestParam(name = "grade", required = true)
  int grade, 
  @Min(value = 1, message = "班級(jí)最小只能1")   
  @Max(value = 99, message = "班級(jí)最大只能99")     
  @RequestParam(name = "classroom", required = true)   
  int classroom) { 
return classroomService.getTeacherName(grade, classroom);
}

如果直接把validator提供的注解移除來(lái)寫(xiě)到請(qǐng)求參數(shù)上面的話是不是就可以了呢?答案是錯(cuò),為什么這樣不能成功的驗(yàn)證參數(shù)呢?具體原因大家可以參考官方文檔:

上面的文檔已經(jīng)說(shuō)的很清楚了,所以我們需要?jiǎng)?chuàng)建一個(gè)Bean

?
1
2
3
4
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() { 
  return new MethodValidationPostProcessor();
}

然后在類方法上面加上注解@Validated

?
1
2
3
4
5
6
@RestController
@RequestMapping("/spring-boot/classroom")
@Validated
public class ClassroomController {
 ...
}

然后之前沒(méi)有生效的注解@Range、@Min、@Max等validator包里面提供的注解就可以生效了。

三、估計(jì)到了這里又會(huì)有人問(wèn)

如果validator包里面注解不能滿足我們的需求,我們是否可以自己定義參數(shù)驗(yàn)證的邏輯。答案是肯定的,我們可以利用

?
1
2
3
4
5
6
7
8
9
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Constraint(validatedBy = {Validator.class})
public @interface ParamValidator {
  String message() default "Parameter error!";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}

?
1
2
3
public class Validator implements ConstraintValidator<ParamValidator, Object> {
  ...
}

組合進(jìn)行自定義,具體的例子網(wǎng)上其他文章就很多了,這里就不進(jìn)行詳細(xì)的例子了,但是最終使用的時(shí)候就是

?
1
2
3
4
5
6
7
8
9
10
@RequestMapping(value = "/paramValidator", method = RequestMethod.GET)
public String paramValidator(
    @ParamValidator(isRequired = true, desc = "年級(jí)", range = "int:1~9", message = "年級(jí)只能從1-9")
    @RequestParam(name = "grade", required = true)
    int grade,
    @ParamValidator(isRequired = true, desc = "班級(jí)", range = "int:1~99", message = "班級(jí)只能從1-99")
    @RequestParam(name = "classroom", required = true)
    int classroom) {
  return classroomService.getTeacherName(grade, classroom);
}

另外不要忘記方法二里面里面提到的MethodValidationPostProcessor這個(gè)bean,如果沒(méi)有初始化這個(gè)bean,自定義的驗(yàn)證方法也不會(huì)執(zhí)行。驗(yàn)證邏輯會(huì)失效。

是不是通過(guò)這樣寫(xiě)注解的方式來(lái)驗(yàn)證進(jìn)行請(qǐng)求的參數(shù),代碼邏輯更佳清晰和優(yōu)雅?表達(dá)的含義也會(huì)更佳清楚?并且沒(méi)有了大量重復(fù)的類似的驗(yàn)證代碼。

Ps:這里的代碼都是基于spring-mvc框架來(lái)試驗(yàn)的,如果有人并沒(méi)有使用spring-mvc作為rest框架,而是使用jersey來(lái)作為rest框架的話,可能一些細(xì)節(jié)方面需要調(diào)整, 但是這三種方案應(yīng)該都是可以兼容的。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://blog.csdn.net/shalousun/article/details/80960835

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 男人天堂网av | 国产这里有精品 | 美女私人影院 | 国产精品视频第一页 | 国产免费午夜 | www.99热| 国产66 | 精品小视频在线观看 | 美女和男人免费网站视频 | narutomanga玖辛奈本子 | 欧美ay| 日韩欧免费一区二区三区 | 亚洲国产第一区二区香蕉日日 | 亚洲精品国产精麻豆久久99 | 日韩精品免费一区二区 | 久久黄色小视频 | 国产精品露脸国语对白99 | 九九免费精品视频 | 欧美va免费大片 | 嫩草影院永久入口在线观看 | 四色6677最新永久网站 | 亚洲精品卡1卡二卡3卡四卡 | 久见久热 这里只有精品 | sxx免费看观看美女 sss亚洲国产欧美一区二区 | gay台湾无套男同志可播放 | 亚洲麻豆精品果冻传媒 | 国内精品视频一区二区三区 | 精品国产一区二区三区久 | 久久理论片迅播影院一级 | 亚洲精品国产A久久久久久 亚洲精品福利一区二区在线观看 | 精选国产AV精选一区二区三区 | 九九九九在线视频播放 | 天天综合五月天 | 好涨好大我快受不了了视频网 | 欧美日韩亚洲国内综合网俺 | 色综合久久九月婷婷色综合 | 日本 视频 在线 | 乌克兰粉嫩摘花第一次 | 国产午夜成人无码免费看 | 无限观看社区在线视频 | 日韩一区二区三区四区区区 |