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

问题

假设SomeInterfaceSomeClass 都属于其他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&lt;SomeInterfaceImpl&gt; 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 方法返回接口的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot实现RESTful接口架构实战(包括REST的讲解定义REST服务测试)

spring boot开发REST接口

spring boot 中访问 REST 接口

spring boot 中访问 REST 接口

Spring Boot REST 验证错误响应

如何正确实现使用 Spring MVC\Boot 返回图像列表的 REST 服务?