JSON Java 中的补丁请求验证
JSON Patch Request validation in Java
在我的 spring 引导服务中,我使用 https://github.com/java-json-tools/json-patch 来处理 PATCH 请求。
除了避免修改对象 ID、creation_time 等不可变字段的方法外,一切似乎都正常。我发现了一个类似的 question on Github https://github.com/java-json-tools/json-patch/issues/21,但我找不到正确的示例。
这个 blog 似乎提供了一些关于验证 JSON 补丁请求的有趣解决方案,其中的解决方案在 node.js 中。很高兴知道 JAVA 中是否已经存在类似的东西。
在许多情况下,您可以只修补一个只有用户可以写入的字段的中间对象。之后,您可以使用某些对象映射器或手动将中间对象轻松映射到您的实体。
这样做的缺点是,如果您要求字段必须显式可为空,您将不知道补丁对象是否将某个字段显式设置为空,或者它是否从未出现在补丁中。
你也可以为此滥用 Optionals,例如
public class ProjectPatchDTO {
private Optional<@NotBlank String> name;
private Optional<String> description;
}
虽然 Optionals 不打算像这样使用,但它是在保持类型化输入的同时实现补丁操作的最直接方式。当可选字段为空时,它从未从客户端传递过来。当可选不存在时,这意味着客户端已将值设置为空。
不是直接从客户端接收 JsonPatch
,而是定义一个 DTO 来处理验证,然后您稍后会将 DTO 实例转换为 JsonPatch
。
假设您要更新实例User.class
的用户,您可以定义一个DTO,例如:
public class UserDTO {
@Email(message = "The provided email is invalid")
private String username;
@Size(min = 2, max = 10, message = "firstname should have at least 2 and a maximum of 10 characters")
private String firstName;
@Size(min = 2, max = 10, message = "firstname should have at least 2 and a maximum of 10 characters")
private String lastName;
@Override
public String toString() {
return new Gson().toJson(this);
}
//getters and setters
}
自定义 toString
方法确保未包含在更新请求中的字段不会预先填充 null
值。
您的PATCH
请求可以如下(为简单起见,我没有迎合例外情况)
@PatchMapping("/{id}")
ResponseEntity<Object> updateUser(@RequestBody @Valid UserDTO request,
@PathVariable String id) throws ParseException, IOException, JsonPatchException {
User oldUser = userRepository.findById(id);
String detailsToUpdate = request.toString();
User newUser = applyPatchToUser(detailsToUpdate, oldUser);
userRepository.save(newUser);
return userService.updateUser(request, id);
}
下面的方法returns在控制器上面更新的补丁用户
private User applyPatchToUser(String detailsToUpdate, User oldUser) throws IOException, JsonPatchException {
ObjectMapper objectMapper = new ObjectMapper();
// Parse the patch to JsonNode
JsonNode patchNode = objectMapper.readTree(detailsToUpdate);
// Create the patch
JsonMergePatch patch = JsonMergePatch.fromJson(patchNode);
// Convert the original object to JsonNode
JsonNode originalObjNode = objectMapper.valueToTree(oldUser);
// Apply the patch
TreeNode patchedObjNode = patch.apply(originalObjNode);
// Convert the patched node to an updated obj
return objectMapper.treeToValue(patchedObjNode, User.class);
}
在我的 spring 引导服务中,我使用 https://github.com/java-json-tools/json-patch 来处理 PATCH 请求。
除了避免修改对象 ID、creation_time 等不可变字段的方法外,一切似乎都正常。我发现了一个类似的 question on Github https://github.com/java-json-tools/json-patch/issues/21,但我找不到正确的示例。
这个 blog 似乎提供了一些关于验证 JSON 补丁请求的有趣解决方案,其中的解决方案在 node.js 中。很高兴知道 JAVA 中是否已经存在类似的东西。
在许多情况下,您可以只修补一个只有用户可以写入的字段的中间对象。之后,您可以使用某些对象映射器或手动将中间对象轻松映射到您的实体。
这样做的缺点是,如果您要求字段必须显式可为空,您将不知道补丁对象是否将某个字段显式设置为空,或者它是否从未出现在补丁中。
你也可以为此滥用 Optionals,例如
public class ProjectPatchDTO {
private Optional<@NotBlank String> name;
private Optional<String> description;
}
虽然 Optionals 不打算像这样使用,但它是在保持类型化输入的同时实现补丁操作的最直接方式。当可选字段为空时,它从未从客户端传递过来。当可选不存在时,这意味着客户端已将值设置为空。
不是直接从客户端接收 JsonPatch
,而是定义一个 DTO 来处理验证,然后您稍后会将 DTO 实例转换为 JsonPatch
。
假设您要更新实例User.class
的用户,您可以定义一个DTO,例如:
public class UserDTO {
@Email(message = "The provided email is invalid")
private String username;
@Size(min = 2, max = 10, message = "firstname should have at least 2 and a maximum of 10 characters")
private String firstName;
@Size(min = 2, max = 10, message = "firstname should have at least 2 and a maximum of 10 characters")
private String lastName;
@Override
public String toString() {
return new Gson().toJson(this);
}
//getters and setters
}
自定义 toString
方法确保未包含在更新请求中的字段不会预先填充 null
值。
您的PATCH
请求可以如下(为简单起见,我没有迎合例外情况)
@PatchMapping("/{id}")
ResponseEntity<Object> updateUser(@RequestBody @Valid UserDTO request,
@PathVariable String id) throws ParseException, IOException, JsonPatchException {
User oldUser = userRepository.findById(id);
String detailsToUpdate = request.toString();
User newUser = applyPatchToUser(detailsToUpdate, oldUser);
userRepository.save(newUser);
return userService.updateUser(request, id);
}
下面的方法returns在控制器上面更新的补丁用户
private User applyPatchToUser(String detailsToUpdate, User oldUser) throws IOException, JsonPatchException {
ObjectMapper objectMapper = new ObjectMapper();
// Parse the patch to JsonNode
JsonNode patchNode = objectMapper.readTree(detailsToUpdate);
// Create the patch
JsonMergePatch patch = JsonMergePatch.fromJson(patchNode);
// Convert the original object to JsonNode
JsonNode originalObjNode = objectMapper.valueToTree(oldUser);
// Apply the patch
TreeNode patchedObjNode = patch.apply(originalObjNode);
// Convert the patched node to an updated obj
return objectMapper.treeToValue(patchedObjNode, User.class);
}