Search code examples
spring-bootgroovyspring-restcontrollerspring-boot-actuator

Spring Boot RestController not returning JSON data


I have an SSCCE that reproduces this problem exactly at my GitHub repo here. To run it locally just:

  1. Clone it
  2. ./gradlew clean build
  3. java -Dspring.config=. -jar build/libs/bootup.jar
  4. Open a browser to http://localhost:9200/cars/1

Essentially I have a Spring Boot app (written in Groovy) that has a single CarController:

@Slf4j
@RestController
@RequestMapping(value = "/cars")
class CarController {
    // Mock for an actual DB
    static Map<Long,Car> carDb
    static {
        carDb = new HashMap<Long,Car>()

        carDb.put(1, new Car(
            1, UUID.fromString("d3bdc4ea-4c62-4bd2-a751-681a531f34f4"),
            new CarType(10, UUID.fromString("ba4dc4ea-4c62-4bd2-a751-681a531f3487"), "Sedan", "SEDAN"),
            "Toyota", "Corolla")
        )
        carDb.put(2, new Car(
            2, UUID.fromString("45a148b9-a44b-41c2-8ad4-c3b5069f091a"),
            new CarType(11, UUID.fromString("b68148b9-a44b-41c2-8ad4-c3b5069f0922"), "Sport Utility Vehicle", "SUV"),
            "Honda", "CRV")
        )
        carDb.put(3, new Car(
            3, UUID.fromString("fe9ede26-886a-4bd9-9b09-535387fffe88"),
            new CarType(12, UUID.fromString("5a5ede26-886a-4bd9-9b09-535387fffe10"), "Truck", "TRUCK"),
            "Chevy", "Silverado")
        )
    }

    @RequestMapping(value = "/{carId}", method = RequestMethod.GET)
    Car getCarById(@PathVariable(value = "carId") Long carId) {
        carDb.get(carId)
    }

    @RequestMapping(method = RequestMethod.POST)
    void createCar(@RequestBody Car car) {
        log.info("Received a car: ${car}")
    }
}

Where Car and CarType, respectively are:

@Canonical
abstract class BaseEntity {
    Long id
    UUID refId
}

@Canonical
@TupleConstructor(includeSuperProperties = true)
@ToString(includeSuperProperties = true)
class Car extends BaseEntity {
    CarType type
    String make
    String model
}

@Canonical
@TupleConstructor(includeSuperProperties = true)
@ToString(includeSuperProperties = true)
class CarType extends BaseEntity {
    String name
    String label
}

I'm using Spring Boot Actuator and here are the dependencies I'm pulling in:

compile(
    'org.codehaus.groovy:groovy-all:2.4.6'
    ,'org.springframework.boot:spring-boot-starter-actuator'
    ,'org.springframework.boot:spring-boot-starter-jetty'
    //,'org.springframework.boot:spring-boot-starter-security'
//        ,'org.springframework.boot:spring-boot-starter-thymeleaf'
    ,'org.apache.commons:commons-lang3:3.4'
    ,'ch.qos.logback:logback-parent:1.1.7'
)
compile('org.springframework.boot:spring-boot-starter-web') {
    exclude module: 'spring-boot-starter-tomcat'
}

dev('org.springframework.boot:spring-boot-devtools')

When I run this (using the steps mentioned above) it starts up just fine. I then open my browser to http://localhost:9200/cars/1 (hoping to get back the first Car in the carDb as a JSON payload) but nothing is returned. In the logs, when I search for that request I see:

2017-04-08 05:06:58.668 DEBUG 31573 --- [tp1164253047-17] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/cars/1]
2017-04-08 05:06:58.675 DEBUG 31573 --- [tp1164253047-17] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /cars/1
2017-04-08 05:06:58.678 DEBUG 31573 --- [tp1164253047-17] s.w.s.m.m.a.RequestMappingHandlerMapping : Returning handler method [public hotmeatballsoup.bootup.model.Car hotmeatballsoup.bootup.controllers.CarController.getCarById(java.lang.Long)]
2017-04-08 05:06:58.678 DEBUG 31573 --- [tp1164253047-17] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/cars/1] is: -1
2017-04-08 05:06:58.751 DEBUG 31573 --- [tp1164253047-17] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2017-04-08 05:06:58.751 DEBUG 31573 --- [tp1164253047-17] o.s.web.servlet.DispatcherServlet        : Successfully completed request
2017-04-08 05:06:58.774 DEBUG 31573 --- [tp1164253047-21] o.s.web.servlet.DispatcherServlet        : DispatcherServlet with name 'dispatcherServlet' processing GET request for [/favicon.ico]
2017-04-08 05:06:58.774 DEBUG 31573 --- [tp1164253047-21] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/favicon.ico] are [/**/favicon.ico]
2017-04-08 05:06:58.775 DEBUG 31573 --- [tp1164253047-21] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/favicon.ico] are {}
2017-04-08 05:06:58.775 DEBUG 31573 --- [tp1164253047-21] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/favicon.ico] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/], class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], class path resource []], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@592dfeae]]] and 1 interceptor
2017-04-08 05:06:58.776 DEBUG 31573 --- [tp1164253047-21] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/favicon.ico] is: -1
2017-04-08 05:06:58.781 DEBUG 31573 --- [tp1164253047-21] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2017-04-08 05:06:58.781 DEBUG 31573 --- [tp1164253047-21] o.s.web.servlet.DispatcherServlet        : Successfully completed request

I'm not sure how to interpret those logs, but I feel like Spring Boot is trying to treat this like a request for HTML (web page) data, not JSON-based REST data.

So I ask: What do I need to do so that a GET request to http://localhost:9200/cars/1 returns JSON, perhaps something like:

{
  "id" : 1,
  "refId" : "d3bdc4ea-4c62-4bd2-a751-681a531f34f4",
  "type" : {
    "id" : 10,
    "refId" : "ba4dc4ea-4c62-4bd2-a751-681a531f3487",
    "name" : "Sedan",
    "label" : "SEDAN"
  }
  "make" : "Toyota",
  "model" : "Corolla"
}

Any ideas?


Solution

  • The problem is car.get(carId ) is returning null. try the following

    @RequestMapping(value = "/{carId}", method = RequestMethod.GET)
    Car getCarById(@PathVariable(value = "carId") Long carId) {
        println 'car >> ' + carDb.get(carId)
        return new Car(
            1, UUID.fromString("d3bdc4ea-4c62-4bd2-a751-681a531f34f4"),
            new CarType(10, UUID.fromString("ba4dc4ea-4c62-4bd2-a751-681a531f3487"), "Sedan", "SEDAN"),
            "Toyota", "Corolla")
    }
    

    initalize your map like this and your original code will work

      static Map<Long,Car> carDb = [(1l): new Car(
                1, UUID.fromString("d3bdc4ea-4c62-4bd2-a751-681a531f34f4"),
                new CarType(10, UUID.fromString("ba4dc4ea-4c62-4bd2-a751-681a531f3487"), "Sedan", "SEDAN"),
                "Toyota", "Corolla"),
            (2l): new Car(
                2, UUID.fromString("45a148b9-a44b-41c2-8ad4-c3b5069f091a"),
                new CarType(11, UUID.fromString("b68148b9-a44b-41c2-8ad4-c3b5069f0922"), "Sport Utility Vehicle", "SUV"),
                "Honda", "CRV"),
            (3l): new Car(
                3, UUID.fromString("fe9ede26-886a-4bd9-9b09-535387fffe88"),
                new CarType(12, UUID.fromString("5a5ede26-886a-4bd9-9b09-535387fffe10"), "Truck", "TRUCK"),
                "Chevy", "Silverado")
        ]