How to solve "Error while extracting response for type [class com.*" in Spring Boot?

19.1k Views Asked by At

You might want to skip to my UPDATE 2 bellow

I have a RestController that works, because when I access it directly from the browser, it returns a JSON response. However, when I send a request from a Service in a different bounded context, I get the error:

{"timestamp":1579095291446,"message":"Error while extracting response for type 
[class com.path.to.contexttwo.client.dto.WorkerDetails] and content type [application/json]; nested exception is 
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: 
Unexpected character ('<' (code 60)): expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false');
nested exception is com.fasterxml.jackson.core.JsonParseException: 
Unexpected character ('<' (code 60)): 
expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\n at [Source: (PushbackInputStream); 
line: 1, column: 2]","details":"uri=/context-two/register-new"}

Here is my code:

RestController

package com.path.to.contextone.aplication.presentation;

@RestController
@RequestMapping(path = "/iacess", produces = "application/json")
@CrossOrigin(origins = "*")
public class IAccessRestController {

    UserRepository userRepo;
    IAcessService iaccessService;
    EntityLinks entityLinks;

    @Autowired
    public IAccessRestController(
            UserRepository userRepo,
            IAcessService iaccessService,
            EntityLinks entityLinks) {
        this.userRepo = userRepo;
        this.iaccessService= iaccessService;
        this.entityLinks = entityLinks;
    }

    @GetMapping("/get-worker-details/{userName}")
    public WorkerDetails getWorkerDetails(@PathVariable String userName) {

        User user = userRepo.findByUsername(userName);

        WorkerDetails workerDetails = new WorkerDetails();
        workerDetails.setUserId(userId);
        workerDetails.setGender(user.gender());
        workerDetails.setFirstName(user.getFirstName());
        workerDetails.setLastName(user.getLastName());
        workerDetails.setPhoneNumber(user.getPhoneNumber());

        if (workerDetails != null) {
            return workerDetails;
        }
        return null;
    }
}

RestClient

package com.path.to.contexttwo.client;

// imports omitted, as well as other code

@Service
public class IAcessRestClientImpl implements IAcessRestClient {

    private final RestTemplate restTemplate;

    @Autowired
    public IAcessRestClientImpl(
            final RestTemplate restTemplate
    ) {
        this.restTemplate = restTemplate;
    }

    @Override
    public WorkerDetails getWorkerDetailsByName(final String userName) throws URISyntaxException {

        Map<String,String> urlVariables = new HashMap<>();
        urlVariables.put("userName", userName);
        return restTemplate.getForObject(
                "http://localhost:8080/iacess/get-worker-details/{userName}",
                WorkerDetails.class,
                urlVariables
        );
    }
}

Config

package com.path.to.contexttwo.configuration;

@Configuration
@EnableWebMvc
public class RestClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        final RestTemplate restTemplate = new RestTemplate();

        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        List<MediaType> mediaTypes = new ArrayList<MediaType>();
        mediaTypes.add(MediaType.APPLICATION_JSON);
        converter.setSupportedMediaTypes(mediaTypes);
        messageConverters.add(converter);
        restTemplate.setMessageConverters(messageConverters);
        restTemplate.getInterceptors().add(new JsonInterceptor());

        return restTemplate;
    }
}

WorkerDetails

package com.path.to.contexttwo.client.dto;

import java.io.Serializable;
import java.util.Objects;

public class WorkerDetails implements Serializable {

    private long userId;
    private String gender;
    private String firstName;
    private String lastName;
    private String phoneNumber;

    public WorkerDetails() {
        this.userId = -1;
        this.gender = null;
        this.firstName = null;
        this.lastName = null;
        this.phoneNumber = null;
    }

    // omitted all args constructor, getters, setters, equals, hascode, toString for simplicity
}

WorkerDetails also exists in package com.path.to.contextone.ohs_pl;

I've been trying for 3 days, reading and debugging, to no avail. Debugger seems to show that the error happens when RestTemplate is analysing the WorkerDetails.class.

I also tried using ComponentScan in all configuration classes, because files are in separate packages (bounded contexts), without success.

I could just use the UserDetailsRepository from the class that calls IAcessRestClient to get the WorkerDetails, but this would make two different bounded contexts depend on the same database schema.

Any help would be very appreciated. I can post aditional code per request.

Thanks in advance

UPDATE @S B ask for input params. here goes the class that sends the params:

CompanyServiceImpl

package com.path.to.contexttwo.domain.services;

// imports

@Service
public class CompanyServiceImpl implements CompanyService {

    private CompanyRepository companyRepository;
    private CompanyWorkerRepositoery companyWorkerRepositoery;
    private WorkerDetailsClient workerDetailsClient;
    private WebApplicationContext applicationContext;

    @Autowired
    CompanyServiceImpl (
            CompanyRepository companyRepository,
            CompanyWorkerRepositoery companyWorkerRepositoery,
            WorkerDetailsClient workerDetailsClient,
            WebApplicationContext applicationContext
    ) {
        this.companyRepository = companyRepository;
        this.companyWorkerRepositoery = companyWorkerRepositoery;
        this.workerDetailsClient = workerDetailsClient;
        this.applicationContext = applicationContext;
    }

    @Transactional
    public Company criateCompany(CompanyDTO dto) throws URISyntaxException { 

        if (dto.getLegalyAuthorized() == true && dto.getTerms() == true) {
            Company company = new Company(
                    dto.getCompanyName(),
                    dto.getStateId()
            );
            company = CompanyRepository.save(company);

            // when saving the company, we also need some details from the current logged in user which can be 
            // retrieved from the idendity and access bounded context. We need those details to be saved in this context            

            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            String name = auth.getName();

            WorkerDetails workerDetails = WorkerDetailsClient.getWorkerDetailsByName(
                    name
            );

            // ... we can't reach the rest of the code anyway, so we omitted            
    }
}

And here is the response I get when acessing the RestController directly:

{"userId":127,"gender":"M","firstName":"Primeiro","lastName":"Último","phoneNumber":"922222222"}

UPDATE 2

Commented out .anyRequest().authenticated() and everything runned OK! So, it has to do with Spring Security all this time. What a shame. Will now try to make things work with security enabled. I was receiving HTML as response because of redirection to login page. Implemented authentication correctly (token request with basic auth) and everything works well.

Thank you all!

1

There are 1 best solutions below

1
On

Try:

return restTemplate.getForObject(
   "http://localhost:8080/iacess/get-worker-details/" + userName,
   WorkerDetails.class);