5
respostas

[Dúvida] Problema na hora de consumir os dados de uma API

Bom estou criando um projeto usando a API: https://opentdb.com/ e estou tendo problema na hora de trazer os dados dela para minha API, pois os dados não estão vindo fica nulo. Vou deixar meu código aqui a baixo pra se alguém poder me ajudar.

Controller:

@RestController
@RequestMapping("/quiz")
@AllArgsConstructor
public class QuizController {

    private final QuizService quizService;

    @PostMapping
    public ResponseEntity<Void> saveQuiz(@RequestBody UrlDTO url) {
        this.quizService.saveQuiz(url);
        return null;
    }
}

Service:

@Service
@AllArgsConstructor
@Slf4j
public class QuizServiceImpl implements QuizService {

    private static final String URL = "https://opentdb.com/api.php?";

    private final QuizRepository quizRepository;
    private final IncorrectAnswerRepository incorrectAnswerRepository;
    private final ConvertData convertData;
    private final ConsumeAPI consumeAPI;

    @Override
    public void saveQuiz(UrlDTO url) {
        try {
            log.info("Buscando dados da API: {}", url);
            var data = consumeAPI.consumeAPI(URL + "amount=" +url.amount() + "&category="
                    + url.category() + "&difficulty=" + url.difficulty() + "&type=" + url.type());

            var quizDTO = convertData.convertData(data, QuizDTO.class);
            log.info("Convertendo dados para o DTO: {}", quizDTO);

            var quiz = new Quiz(quizDTO.type(), quizDTO.difificulty(), quizDTO.category(), quizDTO.question(), quizDTO.correctAnswer());

            List<IncorrectAnswer> incorrectAnswers = quizDTO.incorrectAnswers();
            incorrectAnswers.forEach(incorrectAnswer -> {
                incorrectAnswer.setQuiz(quiz);
                incorrectAnswerRepository.save(incorrectAnswer);
            });
            log.info("Convertendo dados para a entidade: {}", quiz);

            this.quizRepository.save(quiz);
            log.info("Salvando quiz: {}", quiz);
        } catch (Exception e) {
            log.error("Ocorreu um erro ao salvar o quiz: {}", e.getMessage());
            throw new QuizException("Erro ao salvar o quiz", e);
        }
    }

ConsumeApiImpl:

@Service
@Slf4j
public class ConsumeApiImpl implements ConsumeAPI {
    @Override
    public String consumeAPI(String url) {
        if (url == null || url.isEmpty()) {
            throw new IllegalArgumentException("URL is null or empty");
        }
        log.info("Iniciando chamada para a URL: {}", url);
        try {
            URI uri = new URI(url);
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(uri)
                    .build();
            HttpResponse<String> response = client
                    .send(request, HttpResponse.BodyHandlers.ofString());
            log.info("Resposta recebida da API: {}", response.statusCode());
            return response.body();
        } catch (IOException | InterruptedException | URISyntaxException e) {
            log.error("Erro ao consumir a API: {}", e.getMessage());
            throw new ConsumeApiException("Erro ao consumir a API", e);
        }
    }
}

ConvertDataImpl:

@Service
@AllArgsConstructor
public class ConvertDataImpl implements ConvertData {

    private final ObjectMapper objectMapper;

    @Override
    public <T> T convertData(String data, Class<T> clazz) {
        try {
            return objectMapper.readValue(data, clazz);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

QuizDTO:

@JsonIgnoreProperties(ignoreUnknown = true)
public record QuizDTO(
        @JsonAlias("type")
        String type,
        @JsonAlias("difficulty")
        String difificulty,
        @JsonAlias("category")
        String category,
        @JsonAlias("question")
        String question,
        @JsonAlias("correct_answer")
        String correctAnswer,
        @JsonAlias("incorrect_answers")
        List<IncorrectAnswer> incorrectAnswers

) {
}
5 respostas

E aqui vou mostrar os logs mostrando que os dados estão vindo nulos:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

E aqui é como vem o JSON da API:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Olá, Bruno. Sou aluno da Alura, e vou tentar de ajudar, conforme vc compartilhou parte do seu projeto e não ele todo, pois para um comprensão melhor, pode ser necessário que outras partes do código. Mas analisando o que vc passou, e os logs, o problema pode ter relação com a conversão dos dados da resposta da API para o objeto QuizDTO. Quando o ObjectMapper tenta mapear os dados para a classe QuizDTO, ele não encontra os campos corretos na resposta do JSON.

Dando uma olhada na sua classe QuizDTO vi que você está usando o tipo List para representar as respostas incorretas e considerando isso, se a estrutura da resposta JSON está correta e o campo incorrect_answers está sendo retornado como uma lista de strings na resposta JSON da API, então você deve usar uma Lista de Strings List em vez de usar List colocando dentro o Incorrect Answer . Isso deve garantir que as respostas incorretas sejam corretamente mapeadas para uma lista de strings na sua classe QuizDTO. Verifique se a estrutura da resposta JSON da API corresponda a essa definição. Outra coisa que vi no seu QuizDTO, foi uma diferença em um dos atributos, por exemplo, nessa parte do código @JsonAlias("difficulty") String difificulty, há uma diferença entre as expressões dos atributos no JsonAlias está "difficulty" com dois fs, e na definição do atributo está "difificulty", verifique a definição dos atributos. Outro ponto a ser verificado no seu código é o seu controller, ele está retornando como ResponseEntity... pode ser ou não, mas Isso pode ser a causa do retorno nulo em sua chamada. Quando você executa return null;, o Spring Boot interpreta isso como um corpo de resposta vazio, mas não está definindo o status da resposta. Para retornar a resposta adequada, você precisa definir um código de status HTTP, como 200 OK pra que a operação seja bem-sucedida ou algum outro código apropriado, dependendo do contexto. Um exemplo de como seria seu Controller,

``
@RestController

@RequestMapping("/quiz") @AllArgsConstructor public class QuizController {

private final QuizService quizService;

@PostMapping
public ResponseEntity<Void> saveQuiz(@RequestBody UrlDTO url) {
    this.quizService.saveQuiz(url);
    return ResponseEntity.ok().build(); 
}

} ``

Assim sendo, você está retornando uma resposta HTTP adequada após a execução do método saveQuiz do serviço. 
Mas é bom vc se certificar de escolher o código de status HTTP apropriado com base nas regras de negócios do seu projeto. 
Não sei se pude contribuir em te ajudar, mas reafirmo, também estou aprendendo, e as vezes aprendemos debatendo e discutindo ideias e pontos de vista. 
Desejo-lhe sucesso na sua jornada e nos seus projetos!!

Valeu cara fiz as correções que você falou, mas infelizmente ainda permanece o mesmo problema.

Puxa, mano, que chato. As vezes o problema pode ser simples, e achamos que é difícil. Mas procura tentar resolver através das IA"s, chatgpt, gemini, Copilot, a própria IA da Alura, põe seu projeto inteiro lá, a IA faz um diagnóstico e vai encontrar o caminho para a Solução, tenho certeza que vai te ajudar.

Abraço e boa sorte.