如何在Spring Boot中验证JSON请求内容? - Seun

22-11-23 banq

本文中,我们将研究请求验证器库,它能够将用户输入与一组预定义的规则进行比较,并在有错误时返回错误。

依赖:

<dependency>
  <groupId>com.smattme</groupId>
  <artifactId>request-validator</artifactId>
  <version>0.0.2</version>
</dependency>


验证JSON请求体
鉴于我们有一个简单的登录端点,需要一个有效的电子邮件和密码,作为一个好的工程师,我们要确保用户发送的两个字段和电子邮件是有效的。

我们可以通过request-validator库轻松实现这一目标。对于电子邮件输入字段,我们希望用户提供一个非空字段和一个有效的电子邮件地址,而对于密码字段,我们只希望用户提供一个非空值。

@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字符串。

如果API用户提供了一个无效的请求体,他们会收到一个400错误请求,并附有所有数据违规的详细列表。

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
}


请注意,返回的错误列表<String>可以以你和你的团队选择的任何方式作为你的响应的一部分。它并不局限于本文所演示的响应格式。

完整的源代码可在本文末尾找到,它将帮助你更好地理解本教程中的响应格式是如何形成的。

请求验证器库允许我们使用管道(|)字符作为分隔符,将一个或多个规则组合在一起。在上面的清单3.1中,我们使用|将required和email规则组合在一起。

唯一的例外是,当使用regex规则和其他规则时。它应该是最后一条规则,并且应该像||那样用双管字符分开。这是为了适应regex模式中可能存在的管道字符。

Regex Pattern:

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接口并执行自定义逻辑。

PrefixRuleValidator.java

public class PrefixRuleValidator implements RuleValidator {
    private static final String CUSTOM_PREFIX = "custom_";
    @Override
    public ValidationResult isValid(Object value, Rule rule) {
        return value != null && String.class.isAssignableFrom(value.getClass()) &&
                value.toString().startsWith(CUSTOM_PREFIX)
                ? ValidationResult.success()
                : ValidationResult.failed(rule.getKey() + " should start with " + CUSTOM_PREFIX);
    }
}


我们需要的下一个组件是一个将扩展RequestValidator的类。我们将调用这个CustomRequestValidator,而不是库中的RequestValidator,来进行检查。

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()方法的副本,这将有效地使我们的规则与其他默认规则一起使用。

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

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中的编码。

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 上提供了完整的源代码。快乐的编码。