Search code examples
resttemplatespring-restcontrollerspring-restjsonparserfasterxml

RestTemplate - postForObject method - adds entry in database but gives me JsonParseException exception


Why does my PostForObject method fail on the browser when it works locally?
The PostForObject method works for my entity class (i.e. it writes to the database).
But on the browser, my movie server application gives me huge stack trace of exception.

REST Template Controller
package com.skill.controller;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.skill.entity.Movie;

@RestController
public class MovieClientController {

    @Value("${movie-service.baseurl}")
    private String baseURL;
    @RequestMapping(path = "/movies/add/",method=RequestMethod.GET)
    public void addMovie() {
        
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json");
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));

        Movie m1 = new Movie();
        m1.setMno(23);
        m1.setMname("Fan-4");
        m1.setGenre("Fiction");
        m1.setRating(1);

        HttpEntity<Movie> entity = new HttpEntity<>(m1,headers);
        Movie movie = restTemplate.postForObject(baseURL+"/movies/add/", entity, Movie.class);
        System.out.println("The movie id is: " + movie.getMno());

        //ResponseEntity<Movie> response = restTemplate.
        //      exchange(baseURL+"/movies/add/",HttpMethod.POST, entity, Movie.class);
        //return new ResponseEntity<Movie>(response.getBody(),HttpStatus.OK);
        //return new ResponseEntity<String>("Movie 1 saved ", HttpStatus.ACCEPTED);
    }
    @RequestMapping(path = "/movies/")
    public List<Movie> getAllMovies()  {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<?> entity = new HttpEntity<>(headers);
        ResponseEntity<Object[]> responseEntity = restTemplate.getForEntity(baseURL+"/movies/", Object[].class);
        Object[] objects = responseEntity.getBody();
        ObjectMapper mapper = new ObjectMapper();
        return Arrays.stream(objects)
                    .map(object -> mapper.convertValue(object, Movie.class))                      
                    .collect(Collectors.toList());
    }
}
Entity
package com.skill.entity;

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Movie implements Serializable {
//  @Id
//  @Column(name="mno")
    @JsonProperty("mno")
    private Integer mno;
//  @Column(name="mname")
    @JsonProperty("mname")
    private String mname;
//  @Column(name="rating")
    @JsonProperty("rating")
    private Integer rating;
//  @Column(name="genre")
    @JsonProperty("genre")
    private String genre;
    public Movie() {}
    public Movie(Integer mno, String mname, Integer rating, String genre) {
        super();
        this.mno = mno;
        this.mname = mname;
        this.rating = rating;
        this.genre = genre;
    }
    @JsonProperty("mno")
    public Integer getMno() {
        return mno;
    }
    @JsonProperty("mno")
    public void setMno(Integer mno) {
        this.mno = mno;
    }
    @JsonProperty("mname")
    public String getMname() {
        return mname;
    }
    @JsonProperty("mname")
    public void setMname(String mname) {
        this.mname = mname;
    }
    @JsonProperty("rating")
    public Integer getRating() {
        return rating;
    }
    @JsonProperty("rating")
    public void setRating(Integer rating) {
        this.rating = rating;
    }
    @JsonProperty("genre")
    public String getGenre() {
        return genre;
    }
    @JsonProperty("genre")
    public void setGenre(String genre) {
        this.genre = genre;
    }
    @Override
    public String toString() {
        return "Movie [mno=" + mno + ", mname=" + mname + ", rating=" + rating + ", genre=" + genre + "]";
    }
}
Movie Server Application > Post Method > Actual
    //add single movie
    @PostMapping(path = "/movies/add/",consumes="application/json")
    public ResponseEntity<String> addMovie(@RequestBody Movie movie) {
        Integer iRating =  movieService.addMovie(movie);
        if(iRating!=null)
        {
            return new ResponseEntity<String>("Movie saved with rating: " +iRating, HttpStatus.CREATED);
        }
        return new ResponseEntity<String>("Movie not saved", HttpStatus.NOT_ACCEPTABLE);
    }
Browser Stacktrace:
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Wed Jun 16 19:38:55 IST 2021
There was an unexpected error (type=Internal Server Error, status=500).
Error while extracting response for type [class com.skill.entity.Movie] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false') at [Source: (PushbackInputStream); line: 1, column: 7]
org.springframework.web.client.RestClientException: Error while extracting response for type [class com.skill.entity.Movie] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
    at [Source: (PushbackInputStream); line: 1, column: 7]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:120)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:778)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
    at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:437)
    at com.skill.controller.MovieClientController.addMovie(MovieClientController.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false'); nested exception is com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
    at [Source: (PushbackInputStream); line: 1, column: 7]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:389)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105)
    ... 58 more
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Movie': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
    at [Source: (PushbackInputStream); line: 1, column: 7]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2337)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:720)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._reportInvalidToken(UTF8StreamJsonParser.java:3593)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2688)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:870)
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:762)
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4684)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4586)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3601)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:378)
    ... 60 more

Solution

  • You're adding a Accept header which will expect a JSON parse-able entity - Movie in your case - when you call postForObject. headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON))

    But your post method returns a String entity.

    Either return a Movie entity from your post method or edit your consumer to expect a String entity.