In Spring MVC, the requested media type is resolved by a chain of ContentNegotiationStrategy implementations. By default, Spring MVC checks the HTTP Accept header. If no more specific media type is requested, content negotiation can fall back to a default content type or to wildcard matching (*/*), depending on the configuration.
When a wildcard media type is used, Spring selects an HttpMessageConverter that can write the controller's return value. Consequently, when multiple converters can handle the value, the converter configuration and ordering can affect the resulting representation.
If the requested media types cannot be satisfied by any suitable HttpMessageConverter, Spring MVC can return 406 Not Acceptable.
To make the fallback behavior predictable, we can configure a default content type. In the following example, we set JSON as the default media type by using the ContentNegotiationConfigurer#defaultContentType(MediaType...) method in a WebMvcConfigurer.
Example
Setting the default content type
package com.logicbig.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebMvc
@Configuration
@ComponentScan
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(
ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
Writing the controller
package com.logicbig.example;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping
@ResponseBody
public User getUserById (@RequestParam("id") long userId) {
//creating dummy user
User user = new User();
user.setId(userId);
user.setName("joe");
user.setEmailAddress("joe@example.com");
return user;
}
}
package com.logicbig.example;
import tools.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import java.io.Serializable;
@JacksonXmlRootElement
public class User implements Serializable {
private Long id;
private String name;
private String password;
private String emailAddress;
.............
}
Adding the Jackson dependency
We also need to include the Jackson dependency so that a Jackson-based message converter can produce the JSON response. Spring Framework 7 and Spring Boot 4 use Jackson 3 (JacksonJsonHttpMessageConverter, package tools.jackson). On older Spring versions, MappingJackson2HttpMessageConverter with Jackson 2 is used instead.
pom.xml<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.1.0</version>
</dependency>
Writing JUnit tests
In this test, we are not going to send an 'Accept' header, so the default JSON media type will be used for the response. (A 'format' request parameter is not used either, because that strategy is disabled unless favorParameter(true) is set, and path extensions are no longer supported for content negotiation in Spring Framework 7.)
@SpringJUnitWebConfig(MyWebConfig.class)
public class UserTests {
@Autowired
private WebApplicationContext wac;
private MockMvcTester mvc;
@BeforeEach
public void setup() {
this.mvc = MockMvcTester.from(this.wac);
}
@Test
public void testUserRequest() {
assertThat(this.mvc.get()
.uri("/user")
.param("id", "100"))
.hasStatusOk()
.bodyText()
.isEqualTo("{\"emailAddress\":\"joe@example.com\","
+ "\"id\":100,\"name\":\"joe\","
+ "\"password\":null}");
}
}
mvn clean test -Dtest=UserTests#testUserRequest Output (filtered)$ mvn clean test -Dtest=UserTests#testUserRequest [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.UserTests [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.339 s -- in com.logicbig.example.UserTests [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 6.247 s [INFO] Finished at: 2026-09-29T10:51:25-05:00 [INFO] ------------------------------------------------------------------------
As seen in the output, we got JSON in the response body.
Note that a browser (like Chrome) may receive an XML response instead, provided an XML message converter is on the classpath (for example, jackson-dataformat-xml). That is because browsers implicitly send an 'Accept' header listing several media types, including XML with a higher quality value than */*. The HeaderContentNegotiationStrategy can therefore determine the media type, and our default content type is never consulted. The default is only used when the request specifies no media type (or just */*).
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 7.0.6 (Spring TestContext Framework)
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- jackson-databind 3.1.0 (General data-binding functionality for Jackson: works on core streaming API)
- jackson-dataformat-xml 3.1.0 (Data format extension for Jackson to offer
alternative support for serializing POJOs as XML and deserializing XML as POJOs)
- junit-jupiter-engine 6.0.3 (Module "junit-jupiter-engine" of JUnit)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- assertj-core 3.26.3 (Rich and fluent assertions for testing in Java)
- JDK 25
- Maven 3.9.11
|