Back to: Spring Boot Tutorials
Spring Boot RESTful Content Negotiation
In this article, I am going to discuss Spring Boot RESTful Content Negotiation with Examples. Please read our previous article where we discussed Spring Boot HATEOAS with Examples.
What is Content Negotiation?
A resource can have multiple representations to accommodate different clients expecting different formats. Content negotiation is the process of selecting the best representation for a given response and is part of HTTP that allows serving different versions of a document at the same URI. In web APIs, content negotiation is performed server-side to determine the media type format to use when returning a response to an incoming request from the client.
Server-Driven vs. Agent-Driven Content Negotiation
When an algorithm on the server selects the representation for a response, it is called server-driven negotiation. When an agent selects the representation for a response, it is called agent-driven content negotiation. Most REST API implementations rely on agent-driven content negotiations, which use HTTP requests or resource URI patterns to make their selection.
Content negotiation using HTTP Headers
An incoming request may include an entity. The server uses the HTTP ContentType request header to determine the type of entity. Common content types include application/json, application/xml, text/html, and images/jpg. If the request does not include a header, the server can send a preconfigured default representation type.
Content negotiation using URL Patterns
Another way for the client to pass content type information to the server is by using a specific extension in resource URIs. For example, a client can request http://localhost:8080/products.xml instead of http://localhost:8080/products.json to indicate that they want the data in XML format rather than JSON format.
Defining Preferences
The client can define their preference for the content type through the q parameter, which has values between 0 and 1. If the client does not specify the request header, it takes an implicit value of 1. If the client is unsure about their desired representation, they can provide multiple values in the accept header. For example:
Accept: application/json, application/xml;q=0.7,*/*;q=0.5
The above accept header allows you to ask the server for a JSON format. If the JSON format is not present, it will look for the XML format. If the XML format is not possible, let it return what it can.
What happens if XML input is not configured?
All services we are created until now only work with JSON input and JSON output. If we want to send a GET request by using HTTP header application/xml, it will return the status: 406 Not Acceptable. Let us experiment by using the fruits API. Here, we will be using the copy of the fruits API from the “Spring Boot RESTful Web Services” article.
Step 1: Compile and execute the application. Ensure the compilation is successful.
Step 2: Set the body to raw XML in Postman:
Send a request using XML, and the following output will appear. This output shows that:
Also, if you look into the terminal output (of the application), the following output will be visible:
This means that if a client were to send XML data, the application will be unable to process it. This is why XML support is necessary.
How to Add XML Support in Spring Boot?
In this exercise, we will be using the Fruits API written previously.
Step 1: Modify pom.xml to add the jackson-dataformat-xml dependency. To do this, add the following lines in the dependencies section of pom.xml:
Step 2: Compile and execute the application. Ensure the compilation is successful.
Step 3: Resent the same request (from above) via Postman. You should obtain the following output:
This shows that the fruit is created. At this point, the application will process both JSON and XML requests. Congratulations! You now know how to implement content negotiation in Spring Boot.
The Complete Example Code
The complete code is as follows:
Fruits.java – The POJO class
package com.dotnet.restful; public class Fruits { private String id; private String name; public String getId() {return id;} public void setId(String id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} public Fruits (String nid, String nname) { id = nid; name = nname; } }
FruitServiceController.java – The RESTful Controller
package com.dotnet.restful; //Map classes import java.util.HashMap; import java.util.Map; //Required web classes import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class FruitServiceController { private static Map<String, Fruits> productRepo = new HashMap<>(); static { Fruits apple = new Fruits("1","Apple"); productRepo.put(apple.getId(), apple); Fruits banana = new Fruits("2","Banana"); productRepo.put(banana.getId(), banana); Fruits chiku = new Fruits("3","Chiku"); productRepo.put(chiku.getId(), chiku); Fruits dragon = new Fruits("4","Dragon Fruit"); productRepo.put(dragon.getId(), dragon); } @RequestMapping(value = "/products/{id}", method = RequestMethod.DELETE) public ResponseEntity<Object> delete(@PathVariable("id") String id) { if (!productRepo.containsKey(id)) throw new FruitNotFoundException(); productRepo.remove(id); return new ResponseEntity<>("Fruit is deleted!", HttpStatus.OK); } @RequestMapping(value = "/products/{id}", method = RequestMethod.PUT) public ResponseEntity<Object> updateProduct(@PathVariable("id") String id, @RequestBody Fruits product) { if (!productRepo.containsKey(id)) throw new FruitNotFoundException(); productRepo.remove(id); product.setId(id); productRepo.put(id, product); return new ResponseEntity<>("Fruit is updated!", HttpStatus.OK); } @RequestMapping(value = "/products", method = RequestMethod.POST) public ResponseEntity<Object> createProduct(@RequestBody Fruits product) { productRepo.put(product.getId(), product); return new ResponseEntity<>("Fruit is created!", HttpStatus.CREATED); } @RequestMapping(value = "/products") public ResponseEntity<Object> getProduct() { return new ResponseEntity<>(productRepo.values(), HttpStatus.OK); } }
RestfulApplication.java – The class which contains the main() function:
package com.dotnet.restful; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RestfulApplication { public static void main(String[] args) { SpringApplication.run(RestfulApplication.class, args); } }
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.dotnet</groupId> <artifactId>restful</artifactId> <version>0.0.1-SNAPSHOT</version> <name>restful</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
In the next article, I am going to discuss Spring Boot Static Filtering with Examples. Here, in this article, I try to explain Spring Boot RESTful Content Negotiation with Examples. I hope you enjoy this Spring Boot RESTful Content Negotiation article.