spring-bootjacksonopenapifeignresponse-entity

FeignException com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.http.ResponseEntity`


Any Help please !!

I receive this error when I'm calling my endpoint which call Feign in the background :

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of 
`org.springframework.http.ResponseEntity` (no Creators, like default constructor, exist): cannot deserialize 
from Object value (no delegate- or property-based Creator)
 at [Source: (BufferedReader); line: 1, column: 2]

This is my endpoint inside Controller :

@RestController
@RequestMapping(Routes.URI_PREFIX)
public class CartoController {

@Autowired
private ReadCartographyApiDelegate readCartographyApiDelegate;

@GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
                                            @PathVariable(name = "uid") String uid) {
   ResponseEntity<ReadCartographyResponse> result ;
   try {
      result = readCartographyApiDelegate.readCartography(uid);
   }catch (Exception e){
      throw new TechnicalException("Error during read Carto");
   }
   return responseWrapperWithIdBuilder.of(result.getBody());
   }
 }

Interface ReadCartographyApiDelegate generated automatically by openApi from yaml file :

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "...")
public interface ReadCartographyApiDelegate {

    default Optional<NativeWebRequest> getRequest() {
        return Optional.empty();
    }
    default ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
        getRequest().ifPresent(request -> {
            for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                    String exampleString = "null";
                    ApiUtil.setExampleResponse(request, "application/json", exampleString);
                    break;
                }
            }
        });
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);

    }

}

This my ReadCartoApiDelegateImpl which implements ReadCartographyApiDelegate interface :

@Service
public class ReadCartographyApiDelegateImpl implements ReadCartographyApiDelegate {

    private EcomGtmClient ecomGtmClient;

    public ReadCartographyApiDelegateImpl(EcomGtmClient ecomGtmClient) {
        this.ecomGtmClient = ecomGtmClient;
    }

    @Override
    public ResponseEntity<ReadCartographyResponse> readCartography(String uid) {
        ResponseEntity<ReadCartographyResponse> response = ecomGtmClient.readCartography(uid);
        return response;
    }
}

This is the feign client :

@FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
public interface EcomGtmClient {
    @GetMapping(value = "/read-carto/{uid}")
    ResponseEntity<ReadCartographyResponse> readCartography(@PathVariable("uid") String uid);
}

The problem is that ResponseEntity (spring class) class doesn't contain default constructor which is needed during creating of instance. is there Any config to resolve this issue ?


Solution

  • If you want access to the body or headers on feign responses, you should use the feign.Response class. ResponseEntity does not work with feign because it is not meant to. I think it is best if you just return Response from your feign client method. You should then be able to pass the body to the ResponseEntity instance in the Controller.

    What is your reason to even use the response-wrapper, i can't really figure that out from your code?

    Sadly I couldn't find any documentation on the Response class, but here's the link to the source on GitHub. https://github.com/OpenFeign/feign/blob/master/core/src/main/java/feign/Response.java

    My Suggestion would be

    @FeignClient(name = "ecomGtmSvc", url = "http://localhost/")
    public interface EcomGtmClient {
        @GetMapping(value = "/read-carto/{uid}")
        ReadCartographyResponse readCartography(@PathVariable("uid") String uid);
    }
    
    @RestController
    @RequestMapping(Routes.URI_PREFIX)
    public class CartoController {
    
    @Autowired
    private ReadCartographyApiDelegate readCartographyApiDelegate;
    
    @GetMapping(value = "/cartographies/{uid}", produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseWrapper<ReadCartographyResponse> readCarto(HttpServletRequest request,
                                                @PathVariable(name = "uid") String uid) {
       ReadCartographyResponse result ;
       try {
          result = readCartographyApiDelegate.readCartography(uid);
       }catch (Exception e){
          throw new TechnicalException("Error during read Carto");
       }
       // I don't know where you get the builder from, so I assume it does something import and is needed
       return responseWrapperWithIdBuilder.of(result);
       }
     }
    

    Of course you'd also have to change all intermediate classes.