Top 10 Common Spring Boot REST API Mistakes and How to Fix Them
This article examines the ten most frequent errors developers make when building Spring Boot REST APIs, explains the negative impact of each mistake, and offers concrete, up‑to‑date solutions with code examples, while also highlighting a continuously updated collection of over 70 Spring Boot 3 practical case studies.
Environment: SpringBoot 3.2.5
1. Introduction
Building robust and efficient REST APIs is essential for modern application development. Although Spring Boot simplifies this process, developers often make mistakes that can lead to inefficiency, security vulnerabilities, or poor user experience. This article explores the top ten common mistakes in Spring Boot REST APIs, explains their impact, and provides updated solutions using the latest Spring Boot features and best practices.
2. Detailed Errors
2.1 Using Wrong HTTP Status Codes
Error: Returning 200 OK for all responses, including error cases.
Impact: Misleading status codes confuse API clients and make debugging difficult.
Solution: Always use appropriate HTTP status codes based on the situation: 200 OK – request succeeded. 201 Created – resource successfully created. 400 Bad Request – invalid input or validation error. 404 Not Found – resource does not exist. 500 Internal Server Error – unexpected server error.
<code>@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
return productService.findById(id)
.map(product -> ResponseEntity.ok(product))
.orElse(ResponseEntity.status(HttpStatus.NOT_FOUND).build());
}
}
</code>Why it matters: Correct HTTP status codes improve client understanding and API reliability.
2.2 Not Validating Input Data
Error: Accepting data without validation.
Impact: Leads to security vulnerabilities and subsequent errors.
Solution: Use @Valid on DTOs for input validation and handle errors gracefully.
<code>@RestController
@RequestMapping("/api/products")
public class ProductController {
@PostMapping
public ResponseEntity<String> createProduct(@Valid @RequestBody ProductDTO productDTO) {
productService.save(productDTO);
return ResponseEntity.status(HttpStatus.CREATED).body("success");
}
}
public record ProductDTO(@NotNull(message = "名称不能为空") String name,
@Positive(message = "价格必须大于0") Double price) {}
</code>Why it matters: Validation ensures data integrity and prevents abuse.
2.3 Ignoring API Versioning
Error: Developing APIs without version control, causing breaking changes for clients.
Impact: Compatibility issues as the API evolves.
Solution: Implement versioning via URI or custom headers.
<code>@RestController
@RequestMapping("/api/v1/products")
public class ProductV1Controller {
@GetMapping("/{id}")
public Product getProductV1(@PathVariable Long id) {
return productService.findByIdV1(id);
}
}
@RestController
@RequestMapping("/api/v2/products")
public class ProductV2Controller {
@GetMapping("/{id}")
public ProductDTO getProductV2(@PathVariable Long id) {
return productService.findByIdV2(id);
}
}
</code>Why it matters: Versioning preserves backward compatibility while adding new features.
2.4 Hard‑coding Endpoints and Configuration
Error: Hard‑coding URLs or service endpoints in code.
Impact: Makes the application hard to maintain and configure.
Solution: Externalize configuration using application.yml or application.properties .
<code># application.yml
pack:
product:
service:
url: https://api.pack.com/products
</code> <code>@Value("${pack.product.service.url}")
private String productServiceUrl;
</code>Why it matters: Externalized configuration makes the application more flexible and maintainable.
2.5 Improper Exception Handling
Error: Allowing exceptions to propagate to clients without proper formatting.
Impact: Clients receive unclear error messages, causing confusion.
Solution: Use @ExceptionHandler within a @ControllerAdvice for centralized error handling.
<code>@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGenericException(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("发生错误: " + ex.getMessage());
}
}
</code>Why it matters: Proper exception handling improves client experience and debugging efficiency.
2.6 Exposing JPA Entities Directly
Error: Returning database entities directly in API responses.
Impact: Tight coupling between database schema and API.
Solution: Use DTOs to decouple API responses from the database model.
<code>public record ProductDTO(Long id, String name, Double price) {}
public ProductDTO mapToDTO(Product product) {
return new ProductDTO(product.getId(), product.getName(), product.getPrice());
}
</code>Why it matters: DTOs increase API flexibility and prevent sensitive data leakage.
2.7 Missing Pagination and Filtering
Error: Returning large data sets in a single response.
Impact: Performance bottlenecks and client‑side issues.
Solution: Implement pagination and filtering with Pageable .
<code>@GetMapping
public Page<Product> getAllProducts(Pageable pageable) {
return productRepository.findAll(pageable);
}
</code>Why it matters: Pagination and filtering improve API performance and scalability.
2.8 Ignoring API Security
Error: Not protecting the REST API or exposing sensitive data.
Impact: Unauthorized access and potential data leaks.
Solution: Secure the API with Spring Security using JWT or OAuth2.
<code>@Bean
SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Throwable {
http.csrf(csrf -> csrf.disable());
http.authorizeHttpRequests(registry -> {
registry.requestMatchers("/*/*.js", "/*/*.css", "*.css", "*.js", "*.html", "/*/*.html", "/login", "/logout").permitAll();
registry.requestMatchers("/**").authenticated();
});
http.securityMatcher("/api/**", "/admin/**", "/login", "/logout", "/default-ui.css");
http.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
</code>Why it matters: Security protects sensitive data and ensures compliance.
2.9 Skipping API Documentation
Error: Not writing API documentation.
Impact: Other developers find it hard to use the API.
Solution: Use Knife4j (or Swagger) to generate documentation automatically.
<code><dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</code>Why it matters: Documentation boosts developer productivity and collaboration.
2.10 Ignoring HATEOAS
Error: Returning plain JSON without navigation links.
Impact: Clients lack guidance for related actions.
Solution: Use Spring HATEOAS to include navigation links.
<code>import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController
@RequestMapping("/products")
public class ProductController {
@GetMapping("/{id}")
public EntityModel<ProductDTO> queryProductById(@PathVariable Long id) {
ProductDTO productDTO = new ProductDTO(id, "鼠标", 66.6D);
EntityModel<ProductDTO> productModel = EntityModel.of(
productDTO,
linkTo(methodOn(ProductController.class).queryProductById(productDTO.id())).withSelfRel(),
linkTo(methodOn(ProductController.class).queryProducts()).withRel("all-products")
);
return productModel;
}
@GetMapping("")
public List<EntityModel<ProductDTO>> queryProducts() {
return List.of(
EntityModel.of(new ProductDTO(1L, "鼠标", 66.6),
linkTo(methodOn(ProductController.class).queryProductById(1L)).withSelfRel(),
linkTo(methodOn(ProductController.class).queryProducts()).withRel("all-products")),
EntityModel.of(new ProductDTO(2L, "键盘", 88.8),
linkTo(methodOn(ProductController.class).queryProductById(2L)).withSelfRel(),
linkTo(methodOn(ProductController.class).queryProducts()).withRel("all-products"))
);
}
}
</code>Accessing /products/666 yields the following responses:
These examples demonstrate how proper status codes, validation, versioning, externalized configuration, centralized exception handling, DTO usage, pagination, security, documentation, and HATEOAS can greatly improve the quality, maintainability, and usability of Spring Boot REST APIs.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.