Spring 引导 RestController 未返回 JSON 数据
Spring Boot RestController not returning JSON data
我有一个 SSCCE that reproduces this problem exactly at my GitHub repo here。 运行 它在本地只是:
- 克隆它
./gradlew clean build
java -Dspring.config=. -jar build/libs/bootup.jar
- 打开浏览器
http://localhost:9200/cars/1
基本上我有一个 Spring 启动应用程序(用 Groovy 编写),它有一个 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}")
}
}
其中Car
和CarType
分别为:
@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
}
我正在使用 Spring Boot Actuator,这里是我引入的依赖项:
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')
当我 运行 这个(使用上面提到的步骤)时,它启动得很好。然后我打开我的浏览器到 http://localhost:9200/cars/1
(希望取回 carDb
中的第一个 Car
作为 JSON 有效负载)但没有返回任何内容。在日志中,当我搜索该请求时,我看到:
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
我不确定如何解释这些日志,但我觉得 Spring Boot 试图将其视为对 HTML(网页)数据的请求,而不是 JSON 基于 REST 数据。
所以我问:我需要做什么才能向 http://localhost:9200/cars/1
returns JSON 发出 GET 请求,也许类似于:
{
"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"
}
有什么想法吗?
你的控制器方法没有return任何东西
@RequestMapping(value = "/{carId}", method = RequestMethod.GET)
Car getCarById(@PathVariable(value = "carId") Long carId) {
carDb.get(carId)
}
此方法甚至不会编译,因为您将它指定给 return 一辆汽车,但不会 return 任何东西。
可能是您发布了错误的代码,或者您使用的方法看起来不同。您需要做的是 return 那里的 Car 实例,它应该可以工作。
问题是 car.get(carId )
正在返回 null。尝试以下
@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")
}
像这样初始化您的地图,您的原始代码将起作用
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")
]
问题出在你的地图上。您将其定义为 <Long,Car>
但是在添加初始数据时您使用 Integer
用于您的汽车:
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")
)
因此,如果您询问您的地图,键 1L(长)是否是地图的一部分,您将一无所获。但是 1(作为整数)是您地图的一部分,并且会 return 一辆汽车。由于您的 get
方法获得了一个 Long 值,但您所有的键都是整数,因此您永远不会在 HashMap 中找到汽车。您需要使用像这样的长值来初始化您的地图:
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")
)
}
我有一个 SSCCE that reproduces this problem exactly at my GitHub repo here。 运行 它在本地只是:
- 克隆它
./gradlew clean build
java -Dspring.config=. -jar build/libs/bootup.jar
- 打开浏览器
http://localhost:9200/cars/1
基本上我有一个 Spring 启动应用程序(用 Groovy 编写),它有一个 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}")
}
}
其中Car
和CarType
分别为:
@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
}
我正在使用 Spring Boot Actuator,这里是我引入的依赖项:
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')
当我 运行 这个(使用上面提到的步骤)时,它启动得很好。然后我打开我的浏览器到 http://localhost:9200/cars/1
(希望取回 carDb
中的第一个 Car
作为 JSON 有效负载)但没有返回任何内容。在日志中,当我搜索该请求时,我看到:
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
我不确定如何解释这些日志,但我觉得 Spring Boot 试图将其视为对 HTML(网页)数据的请求,而不是 JSON 基于 REST 数据。
所以我问:我需要做什么才能向 http://localhost:9200/cars/1
returns JSON 发出 GET 请求,也许类似于:
{
"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"
}
有什么想法吗?
你的控制器方法没有return任何东西
@RequestMapping(value = "/{carId}", method = RequestMethod.GET)
Car getCarById(@PathVariable(value = "carId") Long carId) {
carDb.get(carId)
}
此方法甚至不会编译,因为您将它指定给 return 一辆汽车,但不会 return 任何东西。 可能是您发布了错误的代码,或者您使用的方法看起来不同。您需要做的是 return 那里的 Car 实例,它应该可以工作。
问题是 car.get(carId )
正在返回 null。尝试以下
@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")
}
像这样初始化您的地图,您的原始代码将起作用
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")
]
问题出在你的地图上。您将其定义为 <Long,Car>
但是在添加初始数据时您使用 Integer
用于您的汽车:
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")
)
因此,如果您询问您的地图,键 1L(长)是否是地图的一部分,您将一无所获。但是 1(作为整数)是您地图的一部分,并且会 return 一辆汽车。由于您的 get
方法获得了一个 Long 值,但您所有的键都是整数,因此您永远不会在 HashMap 中找到汽车。您需要使用像这样的长值来初始化您的地图:
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")
)
}