Capítulo 4.4: Tratamento de Erros
Tempo de estudo: 45 minutos
1. Tratamento de Erros: Protocolos de Emergência do Centro de Controle de Missão (CCM)
No espaço, nem tudo pode sair como planeado: uma explosão solar pode interromper a comunicação, o computador de bordo da nave pode falhar, e a equipa da Terra pode enviar coordenadas incorretas.
O tratamento de erros no frontend são os protocolos de emergência do seu CCM. Eles devem:
- 🚨 Evitar que toda a interface "expluda" devido a um único comando falho.
- 📡 Comunicar claramente ao operador (utilizador) o que exatamente deu errado.
- 🔧 Sugerir possíveis ações futuras.
💡 Analogia espacial:
Se a nave envia um sinal
500 Internal Server Error
, o ecrã do CCM não deve mostrar "Erro Crítico de JavaScript na linha 57". Em vez disso, deve aparecer: "🚨 Falha a bordo da nave! Os engenheiros já foram notificados. Tente repetir o comando mais tarde."
2. Tipos de "Anomalias Espaciais"
No frontend, deparamo-nos com três tipos principais de erros ao trabalhar com a API:
- Erros de Rede: A comunicação com o servidor não foi estabelecida. A antena não funciona, o cabo está cortado.
fetch
"cairá" no bloco.catch()
. - Erros de Cliente (4xx): O comando da Terra estava incorreto. ID inválido, erro de validação. O servidor responde, mas com o status
4xx
. - Erros de Servidor (5xx): Falha na própria nave. Problema no código da API. O servidor responde, mas com o status
500+
.
Já começámos a tratá-los com try...catch
e a verificação de response.ok
. Agora, vamos fazer isso de forma centralizada.
3. Função Centralizada de Manipulação
Repetir o mesmo código try...catch
em cada função é uma má prática. Vamos criar um "wrapper" universal para as nossas requisições fetch
.
Passo 1: Criar api.js
Crie um novo ficheiro api.js
ao lado de app.js
. Nele, moveremos toda a lógica de interação com a API.
// api.js
const API_BASE_URL = 'http://127.0.0.1:8000';
/**
* Função universal para executar requisições à API.
* Lida com erros e retorna JSON.
* @param {string} endpoint - Endpoint da API, por exemplo, '/spaceships'
* @param {object} options - Parâmetros para fetch (method, headers, body)
*/
async function apiRequest(endpoint, options = {}) {
const url = `${API_BASE_URL}${endpoint}`;
try {
const response = await fetch(url, options);
// Se a resposta não for JSON, lança um erro imediatamente
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
// Exceção para requisições DELETE bem-sucedidas que não têm corpo
if (response.status === 204) return null;
throw new TypeError(`Resposta não-JSON recebida do servidor: ${response.statusText}`);
}
const data = await response.json();
if (!response.ok) {
// Se o servidor retornou JSON com um erro (por exemplo, detail do FastAPI)
const errorMessage = data.detail || `Erro HTTP! Status: ${response.status}`;
throw new Error(errorMessage);
}
return data;
} catch (error) {
console.error(`Erro na requisição API para ${endpoint}:`, error);
// Propaga o erro para que possa ser capturado na UI
throw error;
}
}
Passo 2: Conectar api.js
no index.html
É importante conectá-lo ANTES de app.js
, pois app.js
usará as suas funções.
<!-- index.html -->
<body>
<!-- ... -->
<script src="api.js"></script>
<script src="app.js"></script>
</body>
Passo 3: Refatorar app.js
Agora, vamos reescrever as nossas funções usando o novo apiRequest
.
// app.js
// const API_BASE_URL = ...; // Esta linha pode ser removida, agora está em api.js
// ...
async function fetchAndDisplayFleet() {
try {
fleetList.innerHTML = '<li>Carregando telemetria...</li>';
const ships = await apiRequest('/spaceships'); // <-- Usamos nosso wrapper!
fleetList.innerHTML = '';
if (ships.length === 0) {
fleetList.innerHTML = '<li>Não há nenhuma nave no registro.</li>';
return;
}
ships.forEach(ship => { /* ... restante do código de exibição ... */ });
} catch (error) {
fleetList.innerHTML = `<li>🔴 Erro ao carregar a frota: ${error.message}</li>`;
}
}
async function createShip(event) {
event.preventDefault();
const shipData = { /* ... coleta de dados do formulário ... */ };
try {
createStatusMessage.textContent = 'Enviando comando para lançamento...';
const options = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(shipData)
};
const newShip = await apiRequest('/spaceships', options); // <-- Usamos nosso wrapper!
createStatusMessage.textContent = `🚀 Lançamento bem-sucedido! ID atribuído à nave: ${newShip.id}`;
createShipForm.reset();
fetchAndDisplayFleet();
} catch (error) {
createStatusMessage.textContent = `🔴 Erro: ${error.message}`;
}
}
// Reescreva as outras funções (fetchShipById, deleteShip) de forma análoga!
response.ok
e parsing de JSON está num único lugar, e o código em app.js
tornou-se muito mais limpo e legível.
4. Exibição de Erros ao Usuário
Uma boa interface não deve apenas registar o erro na consola, mas também mostrá-lo ao utilizador de forma compreensível.
Exemplo: Melhoria de createShip
O nosso código já faz isso: createStatusMessage.textContent = ...
. Mas podemos fazer ainda melhor, criando uma função universal para exibir notificações.
Adicionando em app.js
:
// app.js
function showNotification(message, isError = false) {
const notificationArea = document.getElementById('create-status-message'); // ou outro elemento
notificationArea.textContent = message;
notificationArea.style.color = isError ? 'red' : 'green';
}
// Usando em createShip:
async function createShip(event) {
// ...
try {
// ...
const newShip = await apiRequest('/spaceships', options);
showNotification(`🚀 Lançamento bem-sucedido! ID: ${newShip.id}`);
// ...
} catch (error) {
showNotification(`🔴 Erro: ${error.message}`, true);
}
}
Quiz para fixação
🚀 Resumo do Capítulo:
Você fortaleceu seu Centro de Controle de Missões, criando protocolos de emergência robustos.
- 🛡️ Você entende a diferença entre erros de rede, de cliente e de servidor.
- ⚙️ Você criou uma função
apiRequest
centralizada para lidar com todas as requisições, evitando duplicação de código. - 📡 Sua interface agora é capaz de informar corretamente o usuário sobre os erros, tornando-a mais amigável e confiável.
Escudos de emergência levantados! Mas o que é melhor: cadeias .then()
ou o moderno async/await
? No próximo capítulo, analisaremos ambas as abordagens e entenderemos quando usar cada uma.
📌 Verificação:
- Verifique se o seu código em
app.js
foi refatorado com sucesso e usa a nova funçãoapiRequest
.- Tente parar o servidor FastAPI e clique no botão "Solicitar Dados". Você deverá ver um erro de conexão na página.
- Tente criar uma nave com dados inválidos. Você deverá ver uma mensagem de erro de validação que veio do FastAPI.
⚠️ Se houver erros:
apiRequest is not defined
: Certifique-se de que você conectouapi.js
emindex.html
antes deapp.js
.- Verifique o console do navegador para outras ocorrências de erros de sintaxe em JavaScript.