Capítulo 2.7: Tratamento de Erros
Tempo de estudo: 40 minutos
1. Por que erros padrão são ruins?
Se ocorrer um erro em sua aplicação Laravel (por exemplo, um registro não encontrado no banco de dados) e você não o tratar de nenhuma forma, o usuário verá uma enorme página HTML com informações de depuração ou uma mensagem não informativa de "Server Error".
Para APIs, isso é uma catástrofe. Seu aplicativo frontend espera receber JSON, não HTML. Nossa tarefa é interceptar qualquer erro e transformá-lo em uma resposta JSON estruturada.
2. Dispatcher Central de Erros: bootstrap/app.php
Em versões antigas do Laravel, havia um arquivo volumoso App\Exceptions\Handler.php
. No Laravel 11/12, tudo se tornou muito mais simples e elegante. O centro de gerenciamento de erros agora está localizado diretamente no arquivo de configuração do seu aplicativo — bootstrap/app.php
.
Abra bootstrap/app.php
. No final, você verá o bloco .withExceptions(...)
. Este é o nosso "dispatcher central".
<?php
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// ...
})
->withExceptions(function (Exceptions $exceptions) {
// <-- É AQUI QUE VAMOS TRABALHAR
})->create();
3. Lidando com o erro mais comum: "Não Encontrado" (404)
O erro mais comum em uma API é quando o usuário solicita um recurso que não existe (por exemplo, GET /api/planets/999
). O Laravel, neste caso, gera uma exceção ModelNotFoundException
ou NotFoundHttpException
. Vamos interceptá-las.
Adicione o seguinte código dentro de .withExceptions(...)
:
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// Interceptamos a exceção quando o modelo não é encontrado no banco de dados
$exceptions->render(function (ModelNotFoundException $e, Request $request) {
// Verificamos se a requisição veio especificamente para nossa API
if ($request->is('api/*')) {
return response()->json([
'message' => 'O recurso solicitado não foi encontrado em nossa galáxia.'
], 404);
}
});
// Interceptamos a exceção quando a própria rota não é encontrada
$exceptions->render(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Essa rota espacial não existe.'
], 404);
}
});
})->create();
$exceptions->render(...)
— registramos um "handler" (manipulador). Ele diz: "Se ocorrer uma exceção do tipoModelNotFoundException
, execute este código".if ($request->is('api/*'))
— esta é uma verificação importante. Ela garante que nossa bela resposta JSON será enviada apenas para requisições de API, sem afetar páginas web comuns.return response()->json(...)
— criamos e retornamos uma resposta JSON padronizada com código 404.
Agora, se você solicitar um planeta inexistente, em vez de uma página HTML feia, você receberá um JSON limpo.
4. Exceções Personalizadas: Criando nossos próprios "sinais de alarme"
Às vezes, as exceções padrão não são suficientes. Imagine que temos uma regra de negócio: "não é possível deletar o planeta 'Terra'". Se alguém tentar fazer isso, devemos retornar um erro significativo.
Passo 1: Criando nossa classe de exceção Executaremos no terminal:
Passo 2: Usando-o no controller
Abriremos PlanetController.php
e modificaremos o método destroy
:
<?php
// app/Http/Controllers/PlanetController.php
use App\Exceptions\CannotDeleteEarthException; // <-- Importamos nossa exceção
use App\Models\Planet;
public function destroy(Planet $planet)
{
// Nossa nova regra de negócio
if (strtolower($planet->name) === 'земля') {
throw new CannotDeleteEarthException('A exclusão do planeta Terra é proibida pelo Código Galáctico.');
}
$planet->delete();
return response()->json(null, 204);
}
DELETE /api/planets/1
(onde 1 é o ID da Terra), nosso código lançará uma exceção CannotDeleteEarthException
.
Passo 3: Ensinando o Laravel a tratar nossa "emergência" de forma elegante
Voltaremos a bootstrap/app.php
e adicionaremos um novo handler para nossa exceção.
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// Nosso novo handler
$exceptions->render(function (CannotDeleteEarthException $e, Request $request) {
return response()->json([
'message' => 'Operação proibida.',
'details' => $e->getMessage() // Obtemos a mensagem que passamos no throw
], 403); // 403 Forbidden - "Acesso Proibido"
});
// ... (outros handlers para 404)
})->create();
5. Tratamento de todas as outras falhas (500 Internal Server Error)
O que fazer com todas as outras falhas inesperadas? Por exemplo, se o banco de dados cair ou houver um erro de sintaxe no código. Para isso, podemos registrar um handler "universal" para o tipo de erro mais geral — Throwable
.
Importante: Este handler deve ser o último, para não interceptar exceções mais específicas que definimos acima.
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// ... (handlers para CannotDeleteEarthException e 404)
// HANDLER UNIVERSAL (no final)
$exceptions->render(function (Throwable $e, Request $request) {
if ($request->is('api/*')) {
// Em modo de depuração, a mensagem de erro real pode ser exibida
$message = config('app.debug')
? 'Ocorreu um erro: ' . $e->getMessage()
: 'Uma falha inesperada ocorreu a bordo. Os engenheiros já foram chamados.';
return response()->json(['message' => $message], 500);
}
});
})->create();
Agora, qualquer exceção "desconhecida" será cuidadosamente interceptada e transformada em JSON com código 500, sem quebrar sua API ou mostrar informações desnecessárias ao usuário.
6. Log de Erros: Caixa preta da nave espacial
Configurações de log em config/logging.php
:
<?php
'channels' => [
'space_api' => [
'driver' => 'daily',
'path' => storage_path('logs/space_api.log'),
'level' => 'error',
'days' => 14,
],
],
Adicionando um registro ao log:
<?php
try {
// Código com risco de erro
} catch (Exception $e) {
Log::channel('space_api')->error('Erro de acesso aos planetas', [
'exception' => $e,
'request' => request()->all(),
'user_id' => auth()->id()
]);
throw $e;
}
Quiz para fixação
🚀 Resumo do capítulo:
Você equipou sua API com um sistema de recuperação robusto:
- 🛟 Interceptação global de erros padrão
- 🪐 Exceções personalizadas com códigos claros
- 📝 Formato JSON unificado para todos os erros
- 🔍 Log com detalhes do incidente
- 📡 Integração com sistemas de monitoramento
A nave espacial está pronta para emergências! No capítulo final da seção, testaremos todos os sistemas.
📌 Verificação:
- Crie uma exceção
PlanetNotFoundException
- Adicione o tratamento de erros 404 em
->withExceptions
- Teste uma requisição para um planeta inexistente
⚠️ Se os erros não forem interceptados:
- Certifique-se de que
is('api/*')
corresponde às suas rotas- Verifique a ordem dos manipuladores em
register()
- Para exceções personalizadas, use
throw new