Exception Handler en Laravel 8

Laravel está muy enfocado a mostrar páginas mediante templates, pero en mi caso lo estoy utilizando para implementar un API. En este caso, la gestión de excepciones que viene de base en Laravel (aquí la documentación oficial) se queda un poco coja porque aunque nos permita devolver un código de error en cualquier momento:

abort(404);

No permite modelar la respuesta de una excepción.

Por tanto, voy a unir todos los puntos que vengo mostrando ya en un par de artículos, que es poder utilizar DTOs que validen su propio modelo; y para esto necesito gestionar las excepciones de una manera similar a como se hace en Spring Boot en Java (y que es muy simple y muy potente).

Para ello vamos a:

  • Definir una excepción que devuelva un JSON.
  • Utilizar la excepción en el DTO

Tenemos la suerte que Laravel 8 ya viene con un Handler por defecto y que en la instalación por defecto podréis encontrar en:

App\Exceptions\Handler

Y definiendo una excepción que devuelva un JSON, ya lo tenemos casi hecho.

Definir una excepción que devuelva un JSON

Lo primero es definir una excepción que podamos lanzar, que a la que le voy a definir 2 atributos: el código HTTP que tiene que devolver y el mensaje.

<?php

namespace App\Exceptions;


use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Mockery\Exception;

class CustomException extends Exception
{

  protected $code;

  protected $message;

  public function __construct($code, $message)
  {
    $this->code = $code;
    $this->message = $message;
  }


  public function render($request){
    $response['message'] = $this->message;
    return response()->json($response, $this->code);
  }
}

Resalto el hecho de que la respuesta que devuelve la excepción mediante el método “render” es un JSON.

Utilizar la excepción en el DTO

Puede parecer magia, y casi lo es, pero ahora ya podemos utilizar la excepción en cualquier lugar y como quiero validar el modelo del DTO en el propio DTO, hay que modificar:

class BookStoreRequestBody extends DataTransferObject
{

  public string $name;

  public string $author;

  public static function fromRequest(Request $request): self
  {
    return BookStoreRequestBody::fromArray($request->all());
  }

  public static function fromArray($request): self
  {
    $validator = Validator::make($request, [
      'name' =>  'required',
      'author' =>  'required',
    ]);
 
    if($validator->fails()){
      return response()->json($validator->errors(), 400);
    }
    return new self([
      'name' => $request['name'],
      'author' => $request['author'],
    ]);
  }
}

La modificamos a:

class BookStoreRequestBody extends DataTransferObject
{

  public string $name;

  public string $author;

  public static function fromRequest(Request $request): self
  {
    return BookStoreRequestBody::fromArray($request->all());
  }

  public static function fromArray($request): self
  {
    $validator = Validator::make($request, [
      'name' =>  'required',
      'author' =>  'required',
    ]);
 
    if($validator->fails()){
      throw new CustomException(400,'Bad request');
    }
    return new self([
      'name' => $request['name'],
      'author' => $request['author'],
    ]);
  }
}

Y con esto ya estaríamos devolviendo la excepción directamente, ya que esa excepción sería capturada por el Handler y devolvería en la API la respuesta del método “render”.

Prueba de la API

Voy a probar la API ahora con Postman (que viene a ser la mejor aplicación para probar una API de largo, si alguien conoce alguna mejor está invitado a comentarla) y ver que efectivamente recibo la respuesta:

Como podéis ver, he pasado de tener que validar en el controlador a tener un DTO que modela la petición y que además incluye la validación de su propio modelo.

Además, ya tenemos una excepción que podemos lanzar desde cualquier punto de código y nos va a dar una respuesta formateada tal y como queremos, en este caso con el código HTTP y el body JSON con la información deseada.

El código del ejemplo, lo podéis ver en github.

Exception Handler en Laravel 8 – Cómo se hace en Spring Boot

En el artículo anterior introduje una manera de implementar DTOs para las entradas y salidas (requests y responses) de una API pero quedó coja porque dentro del DTO pretendía validar el modelo.

Antes de ver la solución en Laravel, me gustaría mostrar la solución a la que quiero llegar pero en otro lenguaje y con otro framework: Spring Boot en Java.

Sigue leyendo Exception Handler en Laravel 8 – Cómo se hace en Spring Boot

Modelos DTO en Laravel 8

Cómo implementar un DTO que modele la petición (request) en Laravel

Los DTO o Data Transfer Object son los tipos de objetos utilizados para modelar la comunicación entre sistemas, lo que en una API REST significa que son los modelos de las peticiones (requests) y respuestas (responses). En la implementación de la API en Laravel, los controladores reciben un objeto genérico Request:

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        // your code here! 
    }

Lo que implica que toda la validación del modelo de datos se debe implementar en el controlador. Buscando referencias y librerías, encontré esta librería para Data Transfer Objects (DTOs): https://github.com/spatie/data-transfer-object y vamos a ver cómo aplicarla para modelar la petición o request.

Sigue leyendo Modelos DTO en Laravel 8

Levantando la infraestructura local con Docker

Hace ya varios años que descubrí Docker y ahora (bueno, desde hace un buen tiempo ya) es una tendencia global: microservicios en contenedores, clusters de contenedores en la nube, etc.

En realidad, esto al 99% de los programadores que conozco ni lo quieren saber ni les interesa: quieren hacer su micro, su back, que funcione y a tirar millas. El problema es que este 99% de programadores ignoran cómo les puede facilitar la vida Docker para multitud de tareas muy básicas y hoy voy a contar una de ellas: montar la infraestructura en local para probar y desarrollar.

Sigue leyendo Levantando la infraestructura local con Docker

Añadir en las trazas las queries de base de datos ejecutadas en Laravel 8

Sigo investigando Laravel 8 y buscando funcionalidad a la que estoy acostumbrado en otros frameworks. Vengo del mundo de C++ y Java, por lo que me tengo que adaptar e investigar cómo resolver en este framework de PHP los problemas comunes que voy encontrando.

Uno de ellos es saber exactamente qué query está ejecutando el framework. Eloquent parece bastante potente pero soy poco amigo de dejar hacer demasiadas cosas al framework y me gusta siempre tener el control, o por lo menos, saber exactamente qué está haciendo “por debajo”. Muy recomendable el artículo “Odio los frameworks” del grandísimo Javi Santana para saber de qué estoy hablando.

Así que, continuando con la anterior entrada en la que customizaba los logs de Laravel, en este caso quiero ver en traza qué query está ejecutando en cada momento.

Sigue leyendo Añadir en las trazas las queries de base de datos ejecutadas en Laravel 8

Trazas o logging customizado en Laravel 8

Sigo trasteando con Laravel 8 para un nuevo proyecto que he arrancado con dos amigos y una las cosas más curiosas es cómo organiza la gestión de los logs. Las trazas son básicas para saber qué está pasando con una aplicación, sobretodo cuando te encuentras con algún comportamiento inesperado o un problema como ya expliqué en un artículo anterior.

Laravel utiliza Monolog (ver código fuente en GitHub) para el formateo de los logs y el formato por defecto sería:

storage/logs/laravel.log

[2020-12-28 00:09:59] local.INFO: mi traza de log

En mi caso, busco:

  • Una traza que incluya los milisegundos, ya que quiero saber en qué parte del procesamiento se pierde más tiempo
  • No me interesa el nombre del canal (en este caso, “local.INFO”)

Sigue leyendo Trazas o logging customizado en Laravel 8

Buenas prácticas de programación: evita los números mágicos

Hoy comenzamos una categoría de artículos explicando buenas prácticas de programación. Es importante tenerlas en cuenta a la hora de escribir código porque será mucho más fácil de leer y mantener en un futuro, tanto por nosotros como por otras personas. Comenzamos con un problema típico: los números mágicos. Sigue leyendo Buenas prácticas de programación: evita los números mágicos