3

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?

halfer
  • 19,824
  • 17
  • 99
  • 186
smeeb
  • 27,777
  • 57
  • 250
  • 447

3 Answers3

4

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")
    ]
dynamo
  • 1,123
  • 10
  • 14
  • Uggghhhh thanks @dynamo (+1) - that did work. Any ideas as to why my static initializer wasn't working for the `carDb` map? Is that the "Groovy" way of doing static init blocks? Thanks again! – smeeb Apr 10 '17 at 13:19
  • Actually the problem was not with the static initalizer, it was the key for your maps you were initializing them with integer but was trying to get them using long – dynamo Apr 10 '17 at 13:22
  • and also could you accept my answer if it solved your problem ? – dynamo Apr 10 '17 at 13:29
  • Lol easy killer...SO makes me wait 24 hours before I can reward a bounty, but its yours! – smeeb Apr 10 '17 at 13:31
1

Your controller method is not returning anything

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

This method would not even compil,e as you specified it to return a Car, but do not return anything. May be you posted the wrong code, or the method you are using looks different. What you need to do, is return an instance of Car there, than it should work.

  • Isele-prefabware this is Groovy, I don't need a `return` statement...also don't forget to check out the exact code in the GitHub repo I linked above. Thats a fully working Spring app that compiles and stands up. – smeeb Apr 09 '17 at 12:06
1

The problem is your map. You define it as <Long,Car> but when adding the initial data you use Integer for your cars:

carDb.put(1, new Car(
        1L, UUID.fromString("d3bdc4ea-4c62-4bd2-a751-681a531f34f4"),
        new CarType(10, UUID.fromString("ba4dc4ea-4c62-4bd2-a751-681a531f3487"), "Sedan", "SEDAN"),
        "Toyota", "Corolla")
    )

So if you ask your map if the key 1L (Long) is part of the map you get nothing. However 1 (as Integer) is part of your map and would return a Car. Since your get method gets a Long value but all your keys are Integers you never find a car in your HashMap. You need to initialise your map with long values like this:

static {
    carDb = new HashMap<Long,Car>()

    carDb.put(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")
    )
    carDb.put(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")
    )
    carDb.put(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")
    )
}
mszalbach
  • 10,612
  • 1
  • 41
  • 53