如何在Spring Boot中驗證JSON請求內容? - Seun

banq發表於2022-11-23

本文中,我們將研究請求驗證器庫,它能夠將使用者輸入與一組預定義的規則進行比較,並在有錯誤時返回錯誤。

依賴:

<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 上提供了完整的原始碼。快樂的編碼。

相關文章