I am getting a type error on a POST controller when I try to send data to it. The error seems to be clear but the solution is escaping me:
Enviroment
Spring Boot 3.1
Java 18
MySQL8
Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
@Entity
@SequenceGenerator(name = "bird_seq", sequenceName = "BIRD_SEQ", initialValue = 100, allocationSize = 50)
@Table(name = "bird")
public class Bird {
@Id
@GeneratedValue(strategy = SEQUENCE, generator = "bird_seq")
private Long id;
@Column(unique = true)
@NotEmpty
private String name;
@Enumerated(EnumType.STRING)
private Sex sex;
@OneToOne(optional = true)
@Fetch(FetchMode.JOIN)
@JoinColumn(name = "current_transmitter_id", nullable = true)
private Transmitter currentTransmitter;
@JsonBackReference
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "bird")
private List<ChickTimer> listChickTimer = new ArrayList<>();
// other fields trimmed
// single property constructor to support testing
public Bird(String name) {
this.name = name;
}
Controller
@RestController
@RequestMapping("/birds")
@RequiredArgsConstructor
public class KiwiController {
@Autowired
private final BirdServiceImpl birdService;
// tried with and without the consumes/produces
// also tried consumes = "application/json"
@PostMapping(value = "/testpost/", consumes = (MediaType.APPLICATION_JSON_VALUE), produces = (MediaType.APPLICATION_JSON_VALUE) )
ResponseEntity<Bird> BirdCreate(@RequestBody Bird newBird) {
Bird bird = birdService.save(newBird);
return new ResponseEntity<>(bird, HttpStatus.CREATED);
}
Unsupported Media Type – Error
{
"timestamp": "2023-09-11T18:29:23.830+00:00",
"status": 415,
"error": "Unsupported Media Type",
"trace": "org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'application/json;charset=UTF-8' is not supportedrntat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:209)rntat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:163)rntat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:136)rntat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122)rntat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179)rntat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146)rntat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)rntat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)rntat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)rntat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)rntat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)rntat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974)rntat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)rntat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)rntat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)rntat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)rntat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)rntat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)rntat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)rntat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)rntat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)rntat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)rntat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)rntat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)rntat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)rntat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)rntat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166)rntat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)rntat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482)rntat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)rntat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)rntat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)rntat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)rntat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)rntat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)rntat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894)rntat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)rntat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)rntat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)rntat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)rntat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)rntat java.base/java.lang.Thread.run(Thread.java:833)rn",
"message": "Content-Type 'application/json;charset=UTF-8' is not supported.",
"path": "/birds/testpost/"
}
Postman
Steps Taken So Far
-
I have another controller on same project working fine (it doesnt even specify media type)
-
Tried with
@PostMapping(value = "/testpost/", consumes = (MediaType.APPLICATION_JSON_VALUE), produces = (MediaType.APPLICATION_JSON_VALUE) )
andconsumes = "application/json"
-
Changed the controller to use another Class I have for Testing – it worked with the test Class
-
Tried to set a breakpoint on the controller on this line
Bird bird = birdService.save(newBird);
but it never gets reached -
Tried manually addding "Accept":"application/json" to postman instead of
*/*
-
Tried curl :
curl -X POST http://localhost:8080/birds/testpost/ –header "Content-Type:application/json" -d ‘{"name":"bob"}’
{"timestamp":"2023-09-11T09:58:02.903+00:00","status":415,"error":"Unsupported Media Type","trace":"org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type ‘application/json;charset=UTF-8’ is not supported
The controller that does work is as simple as:
@PostMapping(value = "/")
ResponseEntity<Pit> newPIT(@RequestBody PitDto pitDto) {
Pit createdPit = pitMapper.toEntity(pitDto);
// some service layer stuff
Pit savedPit = pitService.savePIT(createdPit);
return ResponseEntity.ok().body(savedPit);
}
I don’t even set the application type in that controller (however it does use Dto).
I am out of idea’s but suspect something on the entity since the test entity works in that controller.
Update
After creating a new branch to create a Minimal, Reproducible Example the controller worked. So I changed back to master, last commit, and began to remove the properties.
The field causing a issue is HealthCheck
– here is that class:
@NoArgsConstructor
@ToString
@Entity
@Data
@JsonIgnoreProperties({"hibernateLazyInitalizer", "handler"})
@SequenceGenerator(name = "health_check_seq", sequenceName = "HEALTH_CHECK_SEQ", initialValue = 100, allocationSize = 50)
@Table(name = "health_check")
public class HealthCheck {
@Id
@GeneratedValue(strategy = SEQUENCE, generator = "health_check_seq")
private Long id;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "bird_id")
private Bird bird;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime catchDateTime;
3
Answers
The answer in this case was to do with related/nested entities and the annotations on them for serialization/deserialization (Jackson).
On the bird class there was:
In order to fix two properties with @JsonBackReference I named them:
I also had a
BackReference
on the Many side on one of the classes which I changed to@JsonManagedReference
.The
application/json
error message was a red herring.Remove all
MediaType.APPLICATION_JSON_VALUE
because it is a A String equivalent ofAPPLICATION_JSON
, according to documentation or if you dont want to remove it just replace it toMediaType.APPLICATION_JSON
. Why so ? because i can see in postman you do request to controller with json object not json as stringAnd also be sure that you dont do request with
Content-Type 'application/json;charset=UTF-8
just do it withoututf-8
Also update i see that you also have in one entity
mappedBy = "bird"
two time on different fields, can you change one with another name something likemappedBy = "secondBird"
Your
Bird
class doesn’t have default constructor without parameters. I think Sprig has trouble deserializing your JSON toBird
class because of it. It is a guess, but worth trying. AddTo your class
Bird
or just remove your constructor