Spring Boot RESTful Content Negotiation

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.

Spring Boot RESTful Content Negotiation with Examples

Step 2: Set the body to raw XML in Postman:

Spring Boot RESTful Content Negotiation with Examples

Send a request using XML, and the following output will appear. This output shows that:

Spring Boot RESTful Content Negotiation with Examples

Also, if you look into the terminal output (of the application), the following output will be visible:

Spring Boot RESTful Content Negotiation with Examples

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:

How to Add XML Support in Spring Boot?

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:

How to Add XML Support in Spring Boot?

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.

Leave a Reply

Your email address will not be published. Required fields are marked *