- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章SpringBoot validator参数验证restful自定义错误码响应方式由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
关于spring web应用中关于如何使用 Bean Validation API和hibernate-validator的文章已经很多,本文就不再重复叙述,今天要介绍的重点是在SpringBoot restful服务中如何根据不同验证错误响应不同的自定义错误码。下面直接上代码.
阿里java开发手册中定义的一段参考【“对于公司外的 http/api 开放接口必须使用“错误码”; 而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、 “错误码”、“错误简短信息”。】。因此这里也定义个返回结构.
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;
/**
* 装在数据
*/
private
T data;
/**
* 错误代码
*/
private
String code;
/**
* 默认构造器
*/
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
* 数据
*/
public
CommonResult(
boolean
success, String message, T data){
this
.success = success;
this
.message = message;
this
.data = data;
}
//省略get set
}
|
在有需要国际化的项目,当然选择通过i18n来配置更好,此处为了简单直接采用枚举定义。这里定义的错误仅供参考,不同公司每个应用在实际情况下可能都不大一样.
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
|
/**
* 错误代码枚举类
*
*/
public
enum
ErrorCodeEnum {
SUCCESS(
"0000"
,
"success"
),
PARAM_EMPTY(
"1001"
,
"必选参数为空"
),
PARAM_ERROR(
"1002"
,
"参数格式错误"
),
UNKNOWN_ERROR(
"9999"
,
"系统繁忙,请稍后再试...."
);
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;
}
}
|
静态封装CommonResult主要是方便在项目中快速根据逻辑写返回结果代码.
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
|
/**
* 公共响应结果成功失败的静态方法调用
*
*/
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());
}
}
|
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(
""
);
}
/**
* 根据spring binding 错误信息自定义返回错误码和错误信息
*
* @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);
}
}
}
|
这里直接给出一个简单的参数验证例子.
Controller继承上面写的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
|
/**
* 关于Validator使用测试
*
*/
@RestController
@RequestMapping
(
"validator"
)
public
class
ValidatorTestController
extends
BaseController {
private
static
final
Logger LOGGER = LoggerFactory.getLogger(ValidatorTestController.
class
);
/**
* validate验证测试
*
* @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(
""
);
}
}
|
入参对象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
}
|
这时项目已经已经完全可以根据验证错误来返回自定义的错误码和提示了.
本例所涉及源代码:https://github.com/shalousun/api-doc-test 。
在一些对外服务提供restful的应用中,根据不同的验证错误返回其实是避免不了。当然实现的方式可以有种,而本文所采用的方式相对来说简单易懂.
作为服务端开发,验证前端传入的参数的合法性是一个必不可少的步骤,但是验证参数是一个基本上是一个体力活,而且冗余代码繁多,也影响代码的可阅读性,所以有没有一个比较优雅的方式来解决这个问题?
这么简单的问题当然早就有大神遇到并且解决了,这一篇文章主要讲一下解决基于spring-boot的验证参数的比较好的方法:利用validator-api来进行验证参数.
在spring-boot-starter-web包里面有hibernate-validator包,它提供了一系列验证各种参数的方法,所以说spring-boot已经帮我们想好要怎么解决这个问题了.
这篇文章针对spring-boot里面的spring-mvc介绍三种方式来验证参数.
先假设我们的restful的接口接受一个GradeAndClassroomModel类型的对象,并且这个类被定义成 。
1
2
3
4
5
6
7
|
@Data
public
class
GradeAndClassroomModel {
@Range
(min =
1
, max =
9
, message =
"年级只能从1-9"
)
private
int
grade;
@Range
(min =
1
, max =
99
, message =
"班级只能从1-99"
)
private
int
classroomNumber;
}
|
利用validator提供的一系列注解,比如本例中的@Range,就可以表示参数的范围和出错时候的提示信息。还有很多其他注解,这里就不一一列出 。
然后我们的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());
}
|
其中如果验证出错,result对象里面就会有错误信息,然后可以自己进行处理.
会有人说,就两个参数,为什么要作为对象呢?会不会太麻烦?确实,如果只有少数对象,直接把参数写到Controller层,然后在Controller层进行验证就可以了.
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 =
"年级只能从1-9"
)
@RequestParam
(name =
"grade"
, required =
true
)
int
grade,
@Min
(value =
1
, message =
"班级最小只能1"
)
@Max
(value =
99
, message =
"班级最大只能99"
)
@RequestParam
(name =
"classroom"
, required =
true
)
int
classroom) {
return
classroomService.getTeacherName(grade, classroom);
}
|
如果直接把validator提供的注解移除来写到请求参数上面的话是不是就可以了呢?答案是错,为什么这样不能成功的验证参数呢?具体原因大家可以参考官方文档:
上面的文档已经说的很清楚了,所以我们需要创建一个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 {
...
}
|
然后之前没有生效的注解@Range、@Min、@Max等validator包里面提供的注解就可以生效了.
如果validator包里面注解不能满足我们的需求,我们是否可以自己定义参数验证的逻辑。答案是肯定的,我们可以利用 。
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> {
...
}
|
组合进行自定义,具体的例子网上其他文章就很多了,这里就不进行详细的例子了,但是最终使用的时候就是 。
1
2
3
4
5
6
7
8
9
10
|
@RequestMapping
(value =
"/paramValidator"
, method = RequestMethod.GET)
public
String paramValidator(
@ParamValidator
(isRequired =
true
, desc =
"年级"
, range =
"int:1~9"
, message =
"年级只能从1-9"
)
@RequestParam
(name =
"grade"
, required =
true
)
int
grade,
@ParamValidator
(isRequired =
true
, desc =
"班级"
, range =
"int:1~99"
, message =
"班级只能从1-99"
)
@RequestParam
(name =
"classroom"
, required =
true
)
int
classroom) {
return
classroomService.getTeacherName(grade, classroom);
}
|
另外不要忘记方法二里面里面提到的MethodValidationPostProcessor这个bean,如果没有初始化这个bean,自定义的验证方法也不会执行。验证逻辑会失效.
是不是通过这样写注解的方式来验证进行请求的参数,代码逻辑更佳清晰和优雅?表达的含义也会更佳清楚?并且没有了大量重复的类似的验证代码.
Ps:这里的代码都是基于spring-mvc框架来试验的,如果有人并没有使用spring-mvc作为rest框架,而是使用jersey来作为rest框架的话,可能一些细节方面需要调整, 但是这三种方案应该都是可以兼容的.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/shalousun/article/details/80960835 。
最后此篇关于SpringBoot validator参数验证restful自定义错误码响应方式的文章就讲到这里了,如果你想了解更多关于SpringBoot validator参数验证restful自定义错误码响应方式的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我对编程真的很陌生,并且在理解 RESTful API 的概念时遇到了一些麻烦。我读过 REST 和 RESTful API。我已经查看了 SO 中已经提出的问题,但似乎无法更好地理解该主题。 在我的
我以为我知道REST /“RESTFul”,restfulservices,webservices,SOA和微服务是什么,但是我遇到了许多不同的定义,我得出的结论是这些术语被过度使用,滥用或完全错误定
我有一个列表,其中有一个“人员和组”列。当我使用 REST 查询行时,我会在此列中列出用户 ID。 我发现这篇文章将帮助我将每个 id 转换为标题 http://www.codeproject.com
我想问一些关于 REST 调用的问题。我是 REST 调用的绿色,我想了解什么是 REST 调用以及如何使用 URL 向服务器发送 REST 调用。谁能给我一些基本的教程或链接供我引用? 另外,如果我
很难说出这里问的是什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或言辞激烈,无法以目前的形式合理回答。如需帮助澄清此问题以便可以重新打开,visit the help center . 8年前关闭
如果有一个 REST 资源我想监视来自其他客户端的更改或修改,那么最好(也是最 RESTful)的方法是什么? 我这样做的一个想法是通过提供特定资源来保持连接打开,而不是在资源不(尚)存在时立即返回。
我有一个可以返回大量项目的 RESTful API,我希望能够使用分页样式技术来限制项目数量,这是 RESTful API 中的一个好主意吗? 如果有可能最好通过链接(在这种情况下为 url)或请求正
我仍然处于适应以 REST 方式做事的过程中。 在我的情况下,客户端软件将与 RESTful 服务交互。很少,客户端会上传其整个实体数据库(每个实体序列化为大约 5kb 的 xml 块)。 也许我错了
设计一个路径解析可能有歧义的 REST API 是否被认为是不好的做法?例如: GET /animals/{id} // Returns the animal with the given ID
我知道 REST 并且知道在不使用 session 的情况下创建 RESTful Web 服务,我更了解它,但我不太了解无状态的概念以及使用 REST 如何使您的应用程序可扩展 有人可以解释 REST
我正在尝试找到解决以下问题的最佳方法:我们的应用程序是SaaS,它支持Web登录的SAML。该应用程序还公开了应该在自动化和无人值守的流程中使用的REST API,这意味着没有交互式用户可以键入凭据。
由于 REST 是无状态的,因此传入的每个请求都不知道传入的前一个请求。在这种情况下是否可以使用连接池? 如果要实现连接池,它将像标准数据库连接一样在每个请求时打开连接池并关闭它。 如何实现 REST
得墨忒耳定律(真的应该是得墨忒耳的建议)说你不应该“穿过”一个物体去接触它们的子物体。如果您作为客户需要执行一些重要的操作,大多数情况下您使用的域模型应该支持该操作。 REST 原则上是一个愚蠢的对象
我唯一真正接触到 REST 的想法已经通过 Ruby on Rails 的 RESTful routing .这非常适合我使用 Rails 构建的基于 CRUD 的应用程序,但因此我对 RESTful
有什么好处 http://www.example.com/app/servlet/cat1/cat2/item 网址 超过 http://www.example.com/app/servlet?c
我知道以前有人问过这类问题。我有我的问题的解决方案,我想知道我是否在任何地方破坏了 REST 或 HTTP 主体。 在我的系统中,我有一个名为 member 的资源。支持通常的GET/POST/PUT
我有一个API,可以执行一些批量处理任务。假设它确实为某些资源命名。 我批量传递了7个请求,其中5个更新成功,2个失败。 我的问题是如何应对。使用HTTP时,我无法同时返回成功和错误。 有一个部分成功
我来自 RPC 世界,但目前正在调查使用 REST 是否适合我的项目。至于据我了解 Wikipedia RESTful 服务的基本思想是提供对集合及其各个元素的访问。 在我的情况下,服务器将是一个测量
我想将REST添加到我的挂毯项目中,因此需要知道如何实现它。 有什么更好的方法? 谢谢。 [编辑,从答案中复制:]我必须将GET,PUT,POST和DELETE服务添加到我的挂毯应用程序中。我看到Ta
让 /users/{id}成为 RESTful 服务中的资源 url。 启用基本身份验证,只有经过身份验证的用户才能访问该 url。 示例场景: User_1 & User_2是经过身份验证的用户,用
我是一名优秀的程序员,十分优秀!