使用Hibernate动态模型(无实体类)时如何通过Hibernate Validator验证HTTP请求
Map<String, Object> Request Body with @Valid in Hibernate Dynamic Model Got it, since you're building CRUD with Hibernate's dynamic model and relying on Map<String, Object> instead of traditional entities/DTOs, the standard @Valid annotation won't work out of the box—it’s designed for JavaBeans with getter/setter methods, not generic maps. Here are three actionable ways to add validation to your /signup endpoint:
1. Custom Validation Annotation + Validator
Create a custom annotation that targets Map, then implement a validator to check the map's key-value pairs against your rules (like email format, password length, required fields).
Step 1: Define the Custom Annotation
@Target({ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = UserMapValidator.class) public @interface ValidUserMap { String message() default "Invalid user data"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
Step 2: Implement the Validator
public class UserMapValidator implements ConstraintValidator<ValidUserMap, Map<String, Object>> { private Validator validator; @Override public void initialize(ValidUserMap constraintAnnotation) { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); } @Override public boolean isValid(Map<String, Object> userMap, ConstraintValidatorContext context) { boolean isValid = true; // Validate required fields exist if (userMap.get("email") == null || userMap.get("password") == null) { context.buildConstraintViolationWithTemplate("Email and password are required") .addConstraintViolation(); isValid = false; } else { // Validate email format String email = userMap.get("email").toString(); Set<ConstraintViolation<String>> emailViolations = validator.validate(email, Email.class); if (!emailViolations.isEmpty()) { context.buildConstraintViolationWithTemplate("Invalid email format") .addConstraintViolation(); isValid = false; } // Validate password length String password = userMap.get("password").toString(); if (password.length() < 8) { context.buildConstraintViolationWithTemplate("Password must be at least 8 characters") .addConstraintViolation(); isValid = false; } } // Add more validation rules as needed (username, phone, etc.) return isValid; } }
Step 3: Apply the Annotation to Your Endpoint
First, add @Validated to your controller class (to enable Spring's method-level validation), then use your custom annotation on the Map parameter:
@RestController @Validated // Required for custom parameter validation public class UserController { @PostMapping("/signup") public ResponseEntity<RestResponse> signup(@ValidUserMap @RequestBody Map<String, Object> user) { var restResponse = new RestResponse<>(); restResponse.setStatusCode(HttpStatus.OK.value()); restResponse.setResponse(user); return ResponseEntity.status(HttpStatus.OK) .body(restResponse); } }
2. Manual Validation with Java Bean Validator
If you don't want to create a custom annotation, you can directly inject the Validator into your controller and manually validate each field in the map:
@RestController public class UserController { private final Validator validator; // Inject Validator via constructor public UserController(Validator validator) { this.validator = validator; } @PostMapping("/signup") public ResponseEntity<RestResponse> signup(@RequestBody Map<String, Object> user) { var restResponse = new RestResponse<>(); Set<String> errors = new HashSet<>(); // Check required fields if (user.get("email") == null) { errors.add("Email is required"); } else { // Validate email format String email = user.get("email").toString(); Set<ConstraintViolation<String>> violations = validator.validate(email, Email.class); violations.forEach(v -> errors.add(v.getMessage())); } if (user.get("password") == null) { errors.add("Password is required"); } else { String password = user.get("password").toString(); if (password.length() < 8) { errors.add("Password must be at least 8 characters"); } } // Return errors if any, else proceed if (!errors.isEmpty()) { restResponse.setStatusCode(HttpStatus.BAD_REQUEST.value()); restResponse.setErrors(errors); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(restResponse); } restResponse.setStatusCode(HttpStatus.OK.value()); restResponse.setResponse(user); return ResponseEntity.status(HttpStatus.OK).body(restResponse); } }
3. Use Hibernate Validator's Dynamic Bean Validation
Hibernate Validator allows validating dynamic objects using ValueExtractor and Validator.forExecutables(), though this is more advanced. You can define constraints for specific map keys without writing custom validators for each use case:
// Example of validating a map entry with @Email ValueExtractor<Map<?, ?>> mapValueExtractor = new ValueExtractor<>() { @Override public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) { receiver.value(null, originalValue.get("email")); } }; Validator validator = Validation.byProvider(HibernateValidator.class) .configure() .addValueExtractor(mapValueExtractor) .buildValidatorFactory() .getValidator(); Set<ConstraintViolation<Map<String, Object>>> violations = validator.validate(user); // Handle violations by adding error messages to your response
内容的提问来源于stack exchange,提问作者Noman ali abbasi

