Skip to content

Spring Boot REST API CORS, Content Negotiation, Documentation, and Consuming APIs

Spring uses the Accept header to decide the response format.

Accept: application/json
Accept: 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-xml

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=mobile
@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)

Controller level:

@CrossOrigin(origins = "http://localhost:3000")
@RestController
public class ProductController {
}

Global configuration:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*");
}
}

Dependency:

springdoc-openapi

UI, available automatically once the dependency is added:

/swagger-ui.html

Annotate endpoints for richer documentation:

@Operation(summary = "Get all products")
@GetMapping("/products")
public List<Product> getProducts() {
return service.getProducts();
}

Hide from Swagger’s generated docs:

@Hidden

or

@Operation(hidden = true)

Hide from unauthorized callers entirely (Spring Security):

@PreAuthorize("hasRole('ADMIN')")
RestTemplate restTemplate = new RestTemplate();
Product product =
restTemplate.getForObject(url, Product.class);
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).

  • Content negotiation lets one endpoint serve both JSON and XML based on the Accept header, provided the response media types are declared via produces.
  • Avoid GET request bodies — browsers and proxies don’t reliably support them; use query parameters instead.
  • @CrossOrigin scopes CORS to one controller; a WebMvcConfigurer bean applies it globally.
  • Swagger/OpenAPI (springdoc-openapi) generates interactive API docs automatically; use @Hidden to exclude specific endpoints from those docs, and @PreAuthorize to actually restrict access (documentation hiding alone isn’t a security control).
  • Prefer WebClient over RestTemplate for consuming other APIs — RestTemplate is in maintenance mode and doesn’t support non-blocking/reactive usage.