我们有时会遇到由于用户提供的输入长于数据库列大小甚至不存在的 ENUM 值而导致的服务器错误。不信任用户输入是一种流行的陈词滥调,如果实施,将节省大量时间和资源。
这就是为什么在本文中,我们将研究request-validator库,它能够将用户输入与一组预定义的规则进行比较,并在有错误时返回错误。
依赖安装
为了让我们使用request-validator,我们需要将它添加到我们项目的pom.xml中:
清单 2.1 pom.xml
<dependency>
<groupId>com.smattme</groupId>
<artifactId>request-validator</artifactId>
<version>0.0.2</version>
</dependency>
最新版本的依赖项在Maven 中心可用。
验证 JSON 请求正文
鉴于我们有一个简单的登录端点,需要有效的电子邮件和密码,并且作为一名优秀的工程师,我们希望确保用户发送两个字段并且电子邮件是有效的。
我们可以使用request-validator 库轻松实现这一点。对于电子邮件 输入字段,我们希望用户提供一个非空字段和一个有效的电子邮件地址,而对于密码字段,我们只希望用户提供一个非空值:
清单 3.1 LoginController.JAVA
@RestController
public class LoginController {
@PostMApping("/auth/login")
public ResponseEntity<GenericResponse> login(@RequestBody LoginRequest request) {
Map<String, String> rules = new HashMap<>();
rules.put("email", "required|email");
rules.put("password", "required");
List<String> errors = RequestValidator.validate(request, rules);
if (!errors.isEmpty()) {
GenericResponse genericResponse = new GenericResponse();
genericResponse.setStatus(false);
genericResponse.setCode(
HttpStatus.BAD_REQUEST.value());
genericResponse.setErrors(errors);
genericResponse.setMessage("Missing required parameter(s)");
return ResponseEntity.badRequest().body(genericResponse);
}
//otherwise all is well, process the request
//loginService.login()
return ResponseEntity.ok(
GenericResponse.generic200Responseobj("Login successful"));
}
}
从上面的清单 3.1 中,我们使用Map<String, String>来存储每个预期请求字段的规则。映射的键是 API 用户应提供的字段名称,而值包含验证规则。
然后我们调用RequestValidator.validate()方法来检查传入的请求对象是否符合定义的规则。该方法返回一个List<String>,如果存在违规,它将包含所有错误消息。
该库的一大优势是它为每个规则返回一个单独的描述性错误消息,并在一次调用中检查所有规则。
因为RequestValidator.validate()需要Object数据类型,所以请求对象可以是Map、POJO 甚至是 JSON String。
如果 API 用户提供了无效的请求正文,他们将收到 400 错误请求,其中包含所有数据违规的详细列表:
清单 3.2 curl请求/响应
Request:
curl --location --request POST 'http://localhost:8080/auth/login'
--header 'Content-Type: application/json'
--data-raw '{
"email": "john"
}'
Response:
{
"status": false,
"message": "Missing required parameter(s)",
"errors": [
"password is required",
"email supplied is invalid"
],
"code": 400
}
但是,正如预期的那样,有效的请求正文将返回成功:
清单 3.3 curl请求/响应
Request:
curl --location --request POST 'http://localhost:8080/auth/login'
--header 'Content-Type: application/json'
--data-raw '{
"email": "john@example.com",
"password": "changeit"
}'
Response:
{
"status": true,
"message": "Login successful",
"code": 200
}
请注意,返回的List<String>错误可以以您和您的团队选择的任何方式用作您的响应的一部分。它不限于本文中演示的响应格式。
本文末尾提供了完整的源代码,它将帮助您更好地理解本教程中的响应格式是如何格式化的。
请求验证器库允许我们使用竖线 (|) 字符作为分隔符将一个或多个规则组合在一起。从上面的清单 3.1 中,我们使用|将required和email规则组合在一起。.
唯一的例外是在将正则表达式规则与其他规则一起使用时。它应该是最后一条规则,并且应该用双竖线字符分隔,如 || . 这是为了适应正则表达式模式中管道字符的潜在存在:
清单 3.3 正则表达式模式:
Map<String, String> rules = new HashMap<>();
rules.put("dob", "required||regex:[0-9]{2}-[0-9]{2}-[0-9]{4}");
完整的规则列表可在此处获得。
定义自定义规则
假设我们要添加一个默认不在库中的自定义验证规则,我们可以通过继承 RequestValidator 类并实现RuleValidator接口轻松实现它。
鉴于我们需要添加一条规则以确保用户提供的值以custom_ 开头,首先,我们需要创建一个PrefixRuleValidator 类,该类将实现RuleValidator接口并执行自定义逻辑:
清单 4.1 PrefixRuleValidator.java
public class PrefixRuleValidator implements RuleValidator {
我们需要的下一个组件是一个扩展RequestValidator 的类。 我们将调用这个CustomRequestValidator,而不是库的RequestValidator,来进行我们的检查:
清单 4.2
CustomRequestValidator.java
public class CustomRequestValidator extends RequestValidator {
static {
ruleValidatorMap.put("customprefix", PrefixRuleValidator.class);
}
public static List<String> validate(Object target, Map<String, String> rules) {
String jsonRequest =
convertObjectRequestToJsonString(target);
return validate(jsonRequest, rules, ruleValidatorMap);
}
}
CustomRequestValidator的结构很简单,我们将PrefixRuleValidator类静态添加到父级的ruleValidatorMap中。然后我们继续创建父级的validate()方法的副本,这将有效地使我们的规则与其他默认规则一起可用。
最后,让我们在控制器中使用我们的自定义规则:
清单 4.3
CustomPrefixController.java
@RestController
public class CustomPrefixController {
@PostMapping("/custom")
public ResponseEntity<GenericResponse> formCustomPrefix(@RequestBody Map<String, Object> request) {
Map<String, String> rules = Collections.singletonMap("objectType", "customprefix");
List<String> errors =
CustomRequestValidator.validate(request, rules);
if(!errors.isEmpty()) {
GenericResponse genericResponse = new GenericResponse();
genericResponse.setStatus(false);
genericResponse.setCode(
HttpStatus.BAD_REQUEST.value());
genericResponse.setErrors(errors);
genericResponse.setMessage("Missing required parameter(s)");
return ResponseEntity.badRequest().body(genericResponse);
}
return ResponseEntity.ok(
GenericResponse.generic200ResponseObj("Operation successful"));
}
}
发布有效请求将返回 200 OK:
清单 4.4 Curl请求/响应
Request:
curl --location --request POST 'http://localhost:8080/custom'
--header 'Content-Type: application/json'
--data-raw '{
"objectType": "custom_john"
}'
Response:
{
"status": true,
"message": "Operation successful",
"code": 200
}
另一方面,发布无效请求将返回错误消息,如代码清单 4.1 PrefixRuleValidator.java 所示:
清单 4.5 Curl请求/响应
Request:
curl --location --request POST 'http://localhost:8080/custom'
--header 'Content-Type: application/json'
--data-raw '{
"objectType": "john"
}'
Response:
{
"status": false,
"message": "Missing required parameter(s)",
"errors": [
"objectType should start with custom_"
],
"code": 400
}
结论
在本文中,我们了解了如何轻松验证 JSON 请求正文并确保 API 使用者发送我们期望的数据以及实际示例。完整的源代码可在Github
https://github.com/SeunMatt/smattme-tutorials/tree/master/spring-boot-request-validator上获得。快乐编码。