In my sample application I have a simple @RestController controller:
package app.springtest.api.book;
import app.springtest.service.BookService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping("api/v1/book")
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BookController {
private final BookService bookService;
@PostMapping
public ResponseEntity upsertBook(@Valid @RequestBody BookRequest bookRequest) {
final BookResponse response = bookService.addBook(bookRequest);
return ResponseEntity.ok().body(response);
}
}
It consumes this Json object.
package app.springtest.api.book;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Wither;
import javax.validation.constraints.NotBlank;
@Data
@RequiredArgsConstructor(onConstructor = @__(@JsonCreator))
@Wither
public class BookRequest {
@NotBlank
@JsonProperty(value = "isbn", required = true)
private final String isbn;
@NotBlank
@JsonProperty(value = "name", required = true)
private final String name;
@JsonProperty(value = "author", required = true)
@NotBlank
private final String author;
}
There is nothing special, just Lombok generating some boilerplate code.
However, when I add @EnableWebMvc
annotation to the application configuration, the post request fails and returns this error
{
"timestamp": 1557494204976,
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported",
"path": "/api/v1/book"
}
and there is following error in log:
.c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class app.springtest.api.book.BookRequest]]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type
app.springtest.api.book.BookRequest
: Argument #0 has no property name, is not Injectable: can not use as Creator [constructor for app.springtest.api.book.BookRequest, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode=DEFAULT)}]
How can it be, that the deserialization stops working?
Edit: And why does the @RestController
class work in the situation, when the @EnableWebMvc
is not present? Isn't this the annotation, that makes MVC work in Spring?
When you rely on spring boot autoconfiguration, it automatically discovers custom JsonSerializer, Converter and many other spring mvc stuff. This is handled in WebMvcAutoConfiguration. This autoconfiguration is usually triggered through @SpringBootApplication
As soon as you add @EnableWebMvc
(notice the package name, which comes from spring-web
), it will import DelegatingWebMvcConfiguration
.
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
As soon as this configuration is imported, the annotation @ConditionalOnMissingBean
on WebMvcAutoConfiguration
will no longer be truthy. Therefore, no more autoconfiguration of Spring MVC.
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public class WebMvcAutoConfiguration {
A great way to debug this kind of behavior is to enable debug logging in application.yaml
debug: true