Spring:不使用 Hibernate 的唯一 Id 验证器
Spring: Unique Id validator without using Hibernate
当您执行 POST 时,验证器检查没有重复 ID 的最佳方法是什么?
我试图创建一个自定义验证器,但我总是得到 Internal Server Error
{
"timestamp": "2020-04-25T14:37:19.158+0000",
"status": 500,
"error": "Internal Server Error",
"message": "HV000030: No validator could be found for constraint 'com.omega.mtest.validator.IdConstraint' validating type 'java.lang.Integer'. Check configuration for 'id'",
"path": "/user"
}
这是我的模型class
public class User {
@IdConstraint
@Min(value = 1, message = "ID can't be zero or null")
@Max(value = 1000000, message = "We collect a billion records")
private int id;
@Pattern(regexp = "^[a-zA-Z ]*$", message = "Input doesn't match for a full name")
private String name;
@Min(value = 10)
@Max(value = 120, message = "We didn't expect that age")
private int age;
@Pattern(regexp = "^[a-zA-Z ]*$", message = "Input doesn't match for a city name")
private String city;
public User(int id, String name, int age, String city) {
this.id = id;
this.name = name;
this.age = age;
this.city = city;
}
//getters
}
自定义验证器接口:
@Constraint(validatedBy = UserValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RUNTIME)
public @interface IdConstraint {
String message() default "The input list cannot contain two ugual IDs";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Class接口实现
public class UserValidator implements ConstraintValidator<IdConstraint, List<User>> {
@Override
public void initialize(IdConstraint constraintAnnotation) {
}
@Override
public boolean isValid(List<User> users, ConstraintValidatorContext context) {
if (users.size() == 1) {
return true;
} else {
for (int i = 0; i < users.size(); i++) {
for (int j = 0; j < users.size(); j++) {
if (i != j) {
if (users.get(i).getId() == users.get(j).getId()) {
return true;
}
}
}
}
return false;
}
}
控制器class:
@Validated
@RestController
public class UsersController {
@Autowired
public UserService userService;
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
return new ResponseEntity<>(userService.createUser(user), HttpStatus.CREATED);
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>("Not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/users", method = RequestMethod.GET)
public List<User> getUsers() {
return userService.getAllUsers();
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public User getUserById(@PathVariable int id) {
return userService.findUserById(id);
}
}
用户服务
public interface UserService {
List<User> getAllUsers();
User createUser(User user);
User findUserById(int id);
List<User> list();
}
回购
@Component
public class UserRepository implements UserService {
private List<User> users;
public UserRepository() {
users = new ArrayList<>();
}
@Override
public List<User> getAllUsers() {
return users.stream().collect(Collectors.toList());
}
@Override
public User createUser(User user) {
users.add(user);
return user;
}
@Override
public User findUserById(int id) {
return users.stream()
.filter(t -> t.getId() == id)
.findAny()
.orElse(null);
}
@Override
public List<User> list() {
return users;
}
}
您的验证者签名不正确。
您在这里验证的不是用户列表,而是一个整数。
像这样
public class UserIdValidator implements ConstraintValidator<IdConstraint, Integer> {
那你要问问你的服务是否已经使用了这个id。
public class UserIdValidator implements ConstraintValidator<IdConstraint, Integer> {
@Autowired
Private UserService userService;
@Override
public void initialize(IdConstraint constraintAnnotation) {
}
@Override
public boolean isValid(Integer id, ConstraintValidatorContext context) {
return !userService.existsById(id);
}
}
首先你在错误的地方使用 @IdConstraint
意味着 private int id
。你必须把它放在 List<User>
上。当您有用户列表并且两个用户中的任何一个在请求正文中没有相同的 ID 时,您的验证器就会工作。
您可以通过这两种方式解决。
正在检查服务:
在 post 端点中,您使用一个用户作为请求主体。因此,您可以使用存储库的 getById()
检查服务中的数据库中是否存在 id。如果存在则引发异常否则插入。
User user= userRepository.getById(user.getId());
if (ObjectUtils.isEmpty(user)) {
throw new UserNotFoundException();
}
使用验证器:
在验证器函数中调用存储库并不是更好的方法。
但是,你仍然希望你可以这样做。
public class UserIdValidator implements ConstraintValidator<IdConstraint, Integer> {
@Autowired
Private UserService userService;
@Override
public void initialize(IdConstraint constraintAnnotation) {
}
@Override
public boolean isValid(Integer id, ConstraintValidatorContext context) {
User user= userService.findUserById(id);
if (ObjectUtils.isEmpty(user)) {
return false;
}
return true;
}
}
我这样做了,效果很好:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
Optional<User> check = userService.findUserById(user.getId());
if (check.isPresent()) {
return new ResponseEntity<>(userService.createUser(user), HttpStatus.CREATED);
}
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUserById(@PathVariable int id) {
if (userService.findUserById(id).isPresent()) {
return new ResponseEntity<>(userService.findUserById(id).get(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
当您执行 POST 时,验证器检查没有重复 ID 的最佳方法是什么?
我试图创建一个自定义验证器,但我总是得到 Internal Server Error
{ "timestamp": "2020-04-25T14:37:19.158+0000", "status": 500, "error": "Internal Server Error", "message": "HV000030: No validator could be found for constraint 'com.omega.mtest.validator.IdConstraint' validating type 'java.lang.Integer'. Check configuration for 'id'", "path": "/user" }
这是我的模型class
public class User {
@IdConstraint
@Min(value = 1, message = "ID can't be zero or null")
@Max(value = 1000000, message = "We collect a billion records")
private int id;
@Pattern(regexp = "^[a-zA-Z ]*$", message = "Input doesn't match for a full name")
private String name;
@Min(value = 10)
@Max(value = 120, message = "We didn't expect that age")
private int age;
@Pattern(regexp = "^[a-zA-Z ]*$", message = "Input doesn't match for a city name")
private String city;
public User(int id, String name, int age, String city) {
this.id = id;
this.name = name;
this.age = age;
this.city = city;
}
//getters
}
自定义验证器接口:
@Constraint(validatedBy = UserValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RUNTIME)
public @interface IdConstraint {
String message() default "The input list cannot contain two ugual IDs";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Class接口实现
public class UserValidator implements ConstraintValidator<IdConstraint, List<User>> {
@Override
public void initialize(IdConstraint constraintAnnotation) {
}
@Override
public boolean isValid(List<User> users, ConstraintValidatorContext context) {
if (users.size() == 1) {
return true;
} else {
for (int i = 0; i < users.size(); i++) {
for (int j = 0; j < users.size(); j++) {
if (i != j) {
if (users.get(i).getId() == users.get(j).getId()) {
return true;
}
}
}
}
return false;
}
}
控制器class:
@Validated
@RestController
public class UsersController {
@Autowired
public UserService userService;
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
return new ResponseEntity<>(userService.createUser(user), HttpStatus.CREATED);
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>("Not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/users", method = RequestMethod.GET)
public List<User> getUsers() {
return userService.getAllUsers();
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public User getUserById(@PathVariable int id) {
return userService.findUserById(id);
}
}
用户服务
public interface UserService {
List<User> getAllUsers();
User createUser(User user);
User findUserById(int id);
List<User> list();
}
回购
@Component
public class UserRepository implements UserService {
private List<User> users;
public UserRepository() {
users = new ArrayList<>();
}
@Override
public List<User> getAllUsers() {
return users.stream().collect(Collectors.toList());
}
@Override
public User createUser(User user) {
users.add(user);
return user;
}
@Override
public User findUserById(int id) {
return users.stream()
.filter(t -> t.getId() == id)
.findAny()
.orElse(null);
}
@Override
public List<User> list() {
return users;
}
}
您的验证者签名不正确。
您在这里验证的不是用户列表,而是一个整数。
像这样
public class UserIdValidator implements ConstraintValidator<IdConstraint, Integer> {
那你要问问你的服务是否已经使用了这个id。
public class UserIdValidator implements ConstraintValidator<IdConstraint, Integer> {
@Autowired
Private UserService userService;
@Override
public void initialize(IdConstraint constraintAnnotation) {
}
@Override
public boolean isValid(Integer id, ConstraintValidatorContext context) {
return !userService.existsById(id);
}
}
首先你在错误的地方使用 @IdConstraint
意味着 private int id
。你必须把它放在 List<User>
上。当您有用户列表并且两个用户中的任何一个在请求正文中没有相同的 ID 时,您的验证器就会工作。
您可以通过这两种方式解决。
正在检查服务:
在 post 端点中,您使用一个用户作为请求主体。因此,您可以使用存储库的 getById()
检查服务中的数据库中是否存在 id。如果存在则引发异常否则插入。
User user= userRepository.getById(user.getId());
if (ObjectUtils.isEmpty(user)) {
throw new UserNotFoundException();
}
使用验证器:
在验证器函数中调用存储库并不是更好的方法。 但是,你仍然希望你可以这样做。
public class UserIdValidator implements ConstraintValidator<IdConstraint, Integer> {
@Autowired
Private UserService userService;
@Override
public void initialize(IdConstraint constraintAnnotation) {
}
@Override
public boolean isValid(Integer id, ConstraintValidatorContext context) {
User user= userService.findUserById(id);
if (ObjectUtils.isEmpty(user)) {
return false;
}
return true;
}
}
我这样做了,效果很好:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
Optional<User> check = userService.findUserById(user.getId());
if (check.isPresent()) {
return new ResponseEntity<>(userService.createUser(user), HttpStatus.CREATED);
}
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUserById(@PathVariable int id) {
if (userService.findUserById(id).isPresent()) {
return new ResponseEntity<>(userService.findUserById(id).get(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}