Spring Boot - REST 方法返回接口
Posted
技术标签:
【中文标题】Spring Boot - REST 方法返回接口【英文标题】:Spring Boot - REST method returning interface 【发布时间】:2017-03-23 10:34:51 【问题描述】:背景
我在 Spring Boot (1.4.0.RELEASE) 中有一个简单的 REST API,它不应该连接到数据源,因此需要接收一个对象并将其用于在同一方法中返回另一个对象(接口类型)。这种(某种)不寻常的行为导致我使用 RequestMethod.POST
来同时拥有 @RequestBody 和 @ResponseBody:
@RestController
public class SomeController
@RequestMapping(value = "/path", method = RequestMethod.POST)
public ResponseEntity<SomeInterface> someMethod(@RequestBody SomeClass obj)
SomeInterface toReturn = createSomeInterface(...);
return new ResponseEntity<>(toReturn, HttpStatus.OK);
我还有一个Test
方法:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SomeApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SomeControllerTests
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testSomeMethod()
SomeClass toPost = createPostObj();
ResponseEntity<SomeInterface> respons = this.restTemplate.postForEntity("/path", toPost, SomeInterface.class);
assertEquals(HttpStatus.OK, respons.getStatusCode());
assertNotEquals(null, respons.getBody());
但是,这会引发:
org.springframework.http.converter.HttpMessageNotReadableException:
Could not read document:
Can not construct instance of SomeInterface:
abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
问题
假设SomeInterface
和SomeClass
都属于其他API 并且无法更改,这可以实现吗?
注意
我尝试将SomeInterface
包装到一个类中但没有成功:
public class Wrapper
SomeInterface obj;
// getter and setter ...
【问题讨论】:
SomeInterface toReturn = createSomeInterface(...);
你有 SomeInterface 的实现吗?因为你不能实例化一个java接口。
@IssamELATIF 存在一个实现 SomeInterfaceImpl
,它是外部 API 的一部分。
【参考方案1】:
你的控制器是正确的,问题出在你的测试中。
控制器方法返回一个SomeInterface 实例对象,它是由您正在使用的外部API 中的某个未知类实现的。它可能是一个 POJO 或者可能包含 Jackson 注释,它会自动转换为 JSON/XML 而不会出现问题。
但在测试中,您使用 RestTemplate 来调用您的控制器并将结果绑定到一个新对象。 RestTemplate 需要一个类,而不是接口,才能创建新实例并使用从控制器接收到的 JSON/XML 格式的数据填充它。
如果您无权访问或不知道实现该接口的类以在您的测试中使用它,您可以更改它以检查响应中包含的文本,如"Spring Boot documentation" 中所述,或使用JSON assertion library如果你需要更高级的东西。
【讨论】:
SomeInterface
确实有一个实现 SomeInterfaceImpl
乍一看有点像 POJO 对象(只有 getter 和 setter)。但是,这是从类和接口继承的很长的痕迹,我不确定如何验证对象是否真的可以转换为 JSON/XML 格式(没有 Jackson 注释)。您能否为此测试提供一个具体示例,说明我如何“更改它以检查文本...”?【参考方案2】:
无法构造 SomeInterface 的实例:抽象类型也 需要映射到具体类型,具有自定义反序列化器,或者 包含额外的类型信息
Jackson 抱怨将 SomeInterface.class
作为参数传递给 restTemplate.postForEntity
方法,Jackson 无法实例化接口,而是通过实现。
你的测试应该是这样的
@Test
public void testSomeMethod()
SomeClass toPost = createPostObj();
ResponseEntity<SomeInterface> respons = this.restTemplate.postForEntity("/path", toPost, SomeInterfaceImpl.class);
assertEquals(HttpStatus.OK, respons.getStatusCode());
assertNotEquals(null, respons.getBody());
【讨论】:
必须更改为ResponseEntity<SomeInterfaceImpl> respons = this.restTemplate.postForEntity("/path", toPost, SomeInterfaceImpl.class);
,否则我会遇到编译错误。仍然不起作用,新的堆栈跟踪:org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class SomeInterfaceImpl] and content type [application/json;charset=UTF-8]
.
你的类路径中有com.fasterxml.jackson.core:jackson-databind
吗?
是的。我的 pom.xml 中包含 spring-starter-web
,其中包含 com.fasterxml.jackson.core:jackson-databind:jar:2.8.1:compile
。
请尝试在 pom 文件中添加 com.fasterxml.jackson.core:jackson-databind:jar:2.8.1(没有任何作用域)。以上是关于Spring Boot - REST 方法返回接口的主要内容,如果未能解决你的问题,请参考以下文章