1
resposta

Dúvida API Gutendex. Desafio Catálogo de Livros

Olá, estou fazendo o desafio: Catálogo de Livros, e é um dos requisitos que todos os livros consultados serem salvos no banco de dados, porém todos os títulos de livro que eu testo para a API buscar que contenham acentos ou ''ç'' (Como O Cortiço) ela simplesmente não retorna resultado algum, mesmo esse livro constando na API... Como resolver esse impasse? Em meu código já tentei muitas coisas, mas nada está dando certo, Já mudei a configuração da IntelliJ incluindo: -Dfile.encoding=UTF-8 e também não deu certo. Abaixo está o código do local onde acho que precisará de novas configurações.

obs: não há problemas em digitar títulos com letras maiúsculas e minúsculas

Tenho verificado e meu código parece traz uma URL com problema (para o livro O Cortiço) : https://gutendex.com/books?search=o+cortico

Aqui tenho uma URL que funciona (para o livro O Cortiço): https://gutendex.com/books/?search=o+cortiço

Elas são diferentes... /books/?

Abaixo parte do meu código:

package br.com.sheila.bookcatalog.client;

import br.com.sheila.bookcatalog.dto.BookDTO;
import br.com.sheila.bookcatalog.dto.GutendexResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.Normalizer;

@Service
public class GutendexClient {
    private final RestTemplate restTemplate = new RestTemplate();

    public BookDTO buscarLivroPorTitulo(String tituloOriginal) {
        //Normaliza o título 
        String tituloParaBusca = Normalizer.normalize(tituloOriginal, Normalizer.Form.NFD)
                .replaceAll("\\p{M}", "")
                .toLowerCase();

        //Codifica a URL com o título normalizado
        String url = "https://gutendex.com/books?search=" + URLEncoder.encode(tituloParaBusca, StandardCharsets.UTF_8);

        ResponseEntity<GutendexResponse> resposta = restTemplate.getForEntity(url, GutendexResponse.class);
        GutendexResponse body = resposta.getBody();

        if (body != null && body.results() != null && !body.results().isEmpty()) {
            // A comparação adicional ainda é útil para refinar os resultados localmente
            // caso a API retorne algo próximo, mas não exato.
            String tituloUsuarioNormalizado = Normalizer.normalize(tituloOriginal, Normalizer.Form.NFD)
                    .replaceAll("\\p{M}", "")
                    .toLowerCase();

            return body.results().stream()
                    .filter(dto -> {
                        String tituloDtoNormalizado = Normalizer.normalize(dto.title(), Normalizer.Form.NFD)
                                .replaceAll("\\p{M}", "")
                                .toLowerCase();
                        return tituloDtoNormalizado.contains(tituloUsuarioNormalizado);
                    })
                    .findFirst()
                    .orElse(body.results().get(0));
        }

        return null;
    }
}
1 resposta

Ei, Sheila! Tudo bem?

Agradeço por aguardar o retorno.

Você já identificou algo importante: a diferença entre as URLs (/books?search= vs. /books/?search=). A URL correta é https://gutendex.com/books?search=, e o problema pode está na codificação do título.

No seu código, você utiliza URLEncoder.encode com StandardCharsets.UTF_8, e está ok. Porém, a normalização com Normalizer.Form.NFD e a remoção de diacríticos (\\p{M}) pode estar simplificando excessivamente o título, eliminando acentos e o "ç" antes do envio para a API. Como a Gutendex suporta caracteres acentuados diretamente na consulta, não é necessário remover esses caracteres.

Um exemplo para teste:

    // Codifica a URL com o título original, preservando os acentos
    String url = "https://gutendex.com/books?search=" + URLEncoder.encode(tituloOriginal, StandardCharsets.UTF_8);

    ResponseEntity<GutendexResponse> resposta = restTemplate.getForEntity(url, GutendexResponse.class);
    GutendexResponse body = resposta.getBody();

    if (body != null && body.results() != null && !body.results().isEmpty()) {
        return body.results().stream()
                .filter(dto -> dto.title().toLowerCase().contains(tituloOriginal.toLowerCase()))
                .findFirst()
                .orElse(body.results().get(0));
    }

    return null;
}

Espero que o teste dê certo com essa sugestão. Continue se dedicando aos estudos e qualquer dúvida, compartilhe no fórum.

Até mais, Sheila!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado!