简体   繁体   中英

Spring Boot - REST method returning interface

Background

I've a simple REST API in Spring Boot ( 1.4.0.RELEASE ) which should not be connected to a datasource, and as such, needs to take in an object and use it to return another object ( interface type ) in the same method. This (kind of) unusual behaviour led me to using RequestMethod.POST to both be able to have @RequestBody and @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);
    } 
}

I also have a Test method:

@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());
    }
}

However, this throws:

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

Question

Assuming both SomeInterface and SomeClass are belongings to other API's and can't be changed, is this possible to accomplish?

Note

I've tried to wrap the SomeInterface into a class without success:

public class Wrapper {
    SomeInterface obj;
    // getter and setter ...
}

Your controller is correct, the problem lies in your test.

The controller method returns a SomeInterface instance object, which is implemented by some unknown class in the external API you are using. It's probably a POJO or maybe contains Jackson annotations, and it is automatically converted to JSON/XML without problems.

But in the test you are using RestTemplate to call your controller and bind the result to a new object. RestTemplate needs a class, not an interface, to be able to create the new instance and populate it with the data received from your controller in JSON/XML format.

If you don't have access or know the class that implements that interface to use it in your test, you can change it to check for text contained into the response as explained in the "Spring Boot documentation" or use a JSON assertion library if you need something more advanced.

Can not construct instance of SomeInterface: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

Jackson complains about passing SomeInterface.class as parameter to restTemplate.postForEntity method, Jackson can not instantiate an interface pass an implementation instead.

Your test should look like

@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());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM