Spring Boot REST API CORS, Content Negotiation, Documentation, and Consuming APIs
Content Negotiation
Section titled “Content Negotiation”Spring uses the Accept header to decide the response format.
Accept: application/jsonAccept: application/xml@GetMapping( value = "/products", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })public List<Product> getProducts() { return service.getProducts();}XML support requires an extra dependency:
jackson-dataformat-xmlRequest Body in GET
Section titled “Request Body in GET”Although technically possible, GET request bodies are discouraged because:
- HTTP does not define semantics for GET bodies.
- Proxies may ignore them.
- Browsers generally do not support them.
Preferred approach — use query parameters instead:
GET /products?type=mobileCustom HTTP Status Codes
Section titled “Custom HTTP Status Codes”@PostMapping("/products")public ResponseEntity<Product> createProduct(@RequestBody Product product) {
Product saved = service.save(product);
return ResponseEntity .status(HttpStatus.CREATED) .body(saved);}Or, for a fixed status regardless of return value:
@ResponseStatus(HttpStatus.CREATED)Enabling CORS
Section titled “Enabling CORS”Controller level:
@CrossOrigin(origins = "http://localhost:3000")@RestControllerpublic class ProductController {}Global configuration:
@Configurationpublic class CorsConfig implements WebMvcConfigurer {
public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*"); }}API Documentation with Swagger/OpenAPI
Section titled “API Documentation with Swagger/OpenAPI”Dependency:
springdoc-openapiUI, available automatically once the dependency is added:
/swagger-ui.htmlAnnotate endpoints for richer documentation:
@Operation(summary = "Get all products")@GetMapping("/products")public List<Product> getProducts() { return service.getProducts();}Hiding REST Endpoints
Section titled “Hiding REST Endpoints”Hide from Swagger’s generated docs:
@Hiddenor
@Operation(hidden = true)Hide from unauthorized callers entirely (Spring Security):
@PreAuthorize("hasRole('ADMIN')")Consuming REST APIs
Section titled “Consuming REST APIs”RestTemplate
Section titled “RestTemplate”RestTemplate restTemplate = new RestTemplate();
Product product = restTemplate.getForObject(url, Product.class);WebClient (Recommended)
Section titled “WebClient (Recommended)”WebClient client = WebClient.create();
Product product = client.get() .uri("/products/1") .retrieve() .bodyToMono(Product.class) .block();RestTemplate is in maintenance mode; WebClient is the recommended choice for new code, and supports non-blocking reactive calls (dropping .block() when used in a reactive chain).
Key Takeaways
Section titled “Key Takeaways”- Content negotiation lets one endpoint serve both JSON and XML based on the
Acceptheader, provided the response media types are declared viaproduces. - Avoid GET request bodies — browsers and proxies don’t reliably support them; use query parameters instead.
@CrossOriginscopes CORS to one controller; aWebMvcConfigurerbean applies it globally.- Swagger/OpenAPI (
springdoc-openapi) generates interactive API docs automatically; use@Hiddento exclude specific endpoints from those docs, and@PreAuthorizeto actually restrict access (documentation hiding alone isn’t a security control). - Prefer
WebClientoverRestTemplatefor consuming other APIs —RestTemplateis in maintenance mode and doesn’t support non-blocking/reactive usage.