如何校验请求体JSON是否为空或Null?空对象场景处理方案
Hey there! Let's break down how to handle your request body validation problem, including the tricky {} empty JSON case you mentioned.
First, let's clarify the limitations of built-in tools, then cover solutions for your specific scenario.
1. What Built-in Annotations Can (and Can't) Do
If you're using Spring (a common framework for this use case), the @RequestBody(required = true) annotation will catch:
- A completely missing request body
- Null request bodies
- Blank string bodies like
" "(since Spring treats this as invalid JSON)
But it won't catch an empty JSON object like {}. That's because {} is valid JSON—Spring will bind it to an instance of your DTO class where all fields are null, rather than treating it as a "missing" body.
So there's no out-of-the-box annotation that directly checks for empty JSON objects. But we can build our own, or handle it manually.
2. Custom Validation Annotation (Cleanest Approach)
To validate that the request body isn't null, blank, or an empty JSON object, create a custom annotation and validator.
Step 1: Define the Custom Annotation
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Documented @Constraint(validatedBy = NotEmptyObjectValidator.class) @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface NotEmptyRequestBody { String message() default "Request body cannot be null, blank, or empty"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
Step 2: Implement the Validator Logic
This validator checks if the object is null, or if all its fields are null (indicating an empty {} JSON):
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.springframework.beans.BeanUtils; public class NotEmptyObjectValidator implements ConstraintValidator<NotEmptyRequestBody, Object> { @Override public boolean isValid(Object requestBody, ConstraintValidatorContext context) { // First check for null if (requestBody == null) { return false; } // Check if all non-class fields are null return BeanUtils.getPropertyDescriptors(requestBody.getClass()) .stream() .filter(descriptor -> !"class".equals(descriptor.getName())) .anyMatch(descriptor -> { try { // Return true if any field has a non-null value return descriptor.getReadMethod().invoke(requestBody) != null; } catch (Exception e) { // If reflection fails, treat it as empty return false; } }); } }
Step 3: Use the Annotation in Your Controller
Combine it with @Valid to trigger validation:
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RestController public class MyController { @PostMapping("/process") public String processRequest( @Valid @NotEmptyRequestBody @RequestBody MyRequestDto request ) { // Your business logic here return "Request processed successfully"; } }
3. Manual Check in Controller (Quick Fix)
If you don't want to create a custom annotation, you can add a manual check directly in your controller method:
import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.beans.BeanUtils; @RestController public class MyController { @PostMapping("/process") public ResponseEntity<String> processRequest( @RequestBody(required = true) MyRequestDto request ) { // Check if all fields are null (empty JSON object) boolean isEmptyBody = BeanUtils.getPropertyDescriptors(request.getClass()) .stream() .filter(d -> !"class".equals(d.getName())) .allMatch(d -> { try { return d.getReadMethod().invoke(request) == null; } catch (Exception e) { return true; } }); if (isEmptyBody) { throw new BadRequestException("Request body cannot be empty"); } // Your business logic here return ResponseEntity.ok("Success"); } }
4. Handle Exceptions Globally
To return a clean 400 Bad Request response for all these cases, use a global exception handler:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.http.converter.HttpMessageNotReadableException; @ControllerAdvice public class GlobalExceptionHandler { // Catch validation errors from custom annotations @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<String> handleValidationErrors(MethodArgumentNotValidException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(ex.getBindingResult().getAllErrors().get(0).getDefaultMessage()); } // Catch null/blank request bodies @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<String> handleEmptyBodyErrors(HttpMessageNotReadableException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body("Request body cannot be null or blank"); } // Catch custom BadRequestException from manual checks @ExceptionHandler(BadRequestException.class) public ResponseEntity<String> handleBadRequestErrors(BadRequestException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(ex.getMessage()); } }
(Note: BadRequestException is a custom exception class you'll need to define, extending RuntimeException.)
内容的提问来源于stack exchange,提问作者ujwal sadhu

