4 Common @RequestBody Pitfalls That Can Crash Your Spring Apps

The article explains how @RequestBody binds JSON via HttpMessageConverter using Jackson, then details four frequent issues—empty bodies, extra or unknown fields, date‑format mismatches, and deep nesting causing circular serialization—and provides concrete code‑level solutions such as validation, global exception handling, Jackson configuration, and DTO usage.

CodeNotes
CodeNotes
CodeNotes
4 Common @RequestBody Pitfalls That Can Crash Your Spring Apps

What @RequestBody Actually Does

The core role of @RequestBody is to read the HTTP request body (JSON, XML, etc.) and convert it into a Java object through HttpMessageConverter . Spring Boot uses Jackson as the default JSON serializer/deserializer.

The prerequisite for @RequestBody is that the request body must be JSON (or XML) and the Content-Type header must be application/json (or application/xml ). For form-data or x-www-form-urlencoded , use @RequestParam or @ModelAttribute instead.

1. Pitfall: Empty Request Body

@PostMapping("/order")
public Result createOrder(@RequestBody Order order) {
    // ❌ Empty body leads to 400 Bad Request or null order
    if (order == null) {
        // handle null if framework allows it
    }
    orderService.create(order);
    return Result.success();
}

When the front‑end sends an empty body, Spring throws HttpMessageNotReadableException (400). If the framework is configured to allow null, the method receives null and subsequent calls cause NPE.

Solution:

Include the validation starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Add validation annotations on the DTO and use @Valid:

@Data
public class Order {
    @NotNull
    private Long id;
    @NotBlank
    private String name;
    // ...
}

@PostMapping("/order")
public Result createOrder(@RequestBody @Valid Order order) {
    orderService.create(order);
    return Result.success();
}

Define a global exception handler:

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidation(MethodArgumentNotValidException e) {
        return Result.error("Parameter validation failed: " + e.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleNotReadable() {
        return Result.error("Request body cannot be empty or malformed");
    }
}

2. Pitfall: Extra/Unknown Fields

If the front‑end sends fields that do not exist in the target class, Jackson by default throws UnrecognizedPropertyException. Spring Boot usually disables this behavior, but if the configuration is left enabled the request fails with 400.

Solution 1 – Global ignore:

spring:
  jackson:
    deserialization:
      fail-on-unknown-properties: false

Solution 2 – Annotation on the entity:

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class Order {
    private Long id;
    private String name;
    private BigDecimal amount;
}

3. Pitfall: Date‑Format Parsing Failure

Jackson only recognises ISO‑8601 format for LocalDateTime. A request like {"createTime":"2026-07-06 14:30:00"} triggers InvalidFormatException.

Solution 1 – Field annotation:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

Solution 2 – Global JavaTimeModule registration:

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Configuration
public class JacksonConfig {
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> {
            JavaTimeModule module = new JavaTimeModule();
            module.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            module.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            builder.modules(module);
        };
    }
}

For a few fields, @JsonFormat is the quickest; for many fields, register JavaTimeModule globally.

4. Pitfall: Deep Nesting Causing Circular Serialization

@Data
public class Order {
    private Long id;
    private User user;               // nested
    private List<OrderItem> items;    // nested collection
    private Payment payment;        // nested
    private Address address;        // nested
}

@Data
public class User {
    private Long id;
    private String name;
    private List<Order> orders;      // circular reference!
}

During response serialization, Jackson would recurse infinitely, leading to stack overflow or huge payloads.

Solution 1 – Ignore the back‑reference:

@JsonIgnore
private List<Order> orders;  // ignored in both serialization and deserialization

Solution 2 – Use DTO/VO instead of exposing the entity:

@Data
public class OrderVO {
    private Long id;
    private Long userId;          // only the ID, not the full User object
    private List<OrderItemVO> items;
}

Solution 3 – Explicitly mark the relationship:

// User.java
@JsonManagedReference
private List<Order> orders;

// Order.java
@JsonBackReference
private User user;

The most robust approach is to keep entities internal and expose DTOs at the API layer.

Summary

@RequestBody works via HttpMessageConverter with Jackson as the default mapper.

Empty bodies → 400 / NPE; handle with global exception handling and @Valid field checks.

Unknown fields → configure fail-on-unknown-properties: false or use @JsonIgnoreProperties.

Date format issues → @JsonFormat or register JavaTimeModule globally.

Deep nesting → avoid exposing entities; use DTO/VO, @JsonIgnore, or @JsonManagedReference/@JsonBackReference.

Always ensure the request’s Content-Type matches the body format (typically application/json).

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DTOValidationSpring BootJacksonRequestBodyJavaTimeModule
CodeNotes
Written by

CodeNotes

Discuss code and AI, and document daily life and personal growth.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.