gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Ahora que tenemos una nueva página, en tu terminal, ejecuta de nuevo debug:router
.
php bin/console debug:router
Sí, ¡ahí está nuestra nueva ruta! Observa que la tabla tiene una columna llamada "Método" que dice "cualquiera". Esto significa que puedes hacer una petición a esta URL utilizando cualquier método HTTP -como GET o POST- y coincidirá con esa ruta.
Pero el objetivo de nuestra nueva ruta API es permitir a los usuarios hacer una petición GET para obtener datos de la canción. Técnicamente, ahora mismo, también podrías hacer una petición POST a esto... y funcionaría perfectamente. Puede que no nos importe, pero a menudo con las APIs, querrás restringir una ruta para que sólo funcione con un método específico como GET, POST o PUT. ¿Podemos hacer que esta ruta, de alguna manera, sólo funcione con peticiones GET?
Sí! Añadiendo otra opción a la Route
. En este caso, se llama methods
, ¡incluso se autocompleta! Establece esto como un array y, pon GET
.
... lines 3 - 9 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id}', methods: ['GET']) ( | |
public function getSong($id): Response | |
{ | |
... lines 15 - 22 | |
} | |
} |
Voy a mantener pulsado Comando y a hacer clic en la clase Route
de nuevo... para que podamos ver que... ¡sí! methods
es uno de los argumentos.
Volvemos a debug:router
:
php bin/console debug:router
Bien. La ruta ahora sólo coincidirá con las peticiones GET. Es... un poco difícil probar esto, ya que un navegador siempre hace peticiones GET si vas directamente a una URL... pero aquí es donde otro comando de bin/console
resulta útil: router:match
.
Si lo ejecutamos sin argumentos
php bin/console router:match
Nos da un error, ¡pero muestra cómo se utiliza! Inténtalo:
php bin/console router:match /api/songs/11
Y... ¡eso coincide con nuestra nueva ruta! Pero ahora pregúntate qué pasaría si hiciéramos una petición POST a esa URL con --method=POST
:
php bin/console router:match /api/songs/11 --method=POST
¡Ninguna ruta coincide con esta ruta con ese método! Pero dice que casi coincide con nuestra ruta.
Vamos a hacer una cosa más para restringir nuestra nueva ruta. Voy a añadir una pista de tipo int
al argumento $id
.
... lines 3 - 9 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id}', methods: ['GET']) ( | |
public function getSong(int $id): Response | |
{ | |
... lines 15 - 22 | |
} | |
} |
Eso... no cambia nada, excepto que ahora PHP tomará la cadena id
de la URL que Symfony pasa a este método y la convertirá en un int
, lo cual es... agradable porque entonces estamos tratando con un verdadero número entero en nuestro código.
Puedes ver la sutil diferencia en la respuesta. Ahora mismo, el campo id
es una cadena. Cuando actualizamos, id
es ahora un número verdadero en JSON.
Pero... si alguien se hiciera el remolón... y pasara a /api/songs/apple
... ¡vaya! ¡Un error PHP, que, en producción, sería una página de error 500! Eso no me gusta.
Pero... ¿qué podemos hacer? El error se produce cuando Symfony intenta llamar a nuestro controlador y le pasa ese argumento. Así que no podemos poner código en el controlador para comprobar si $id
es un número: ¡es demasiado tarde!
¿Y si, en cambio, pudiéramos decirle a Symfony que esta ruta sólo debe coincidir si el comodín id
es un número? ¿Es posible? Totalmente
Por defecto, cuando tienes un comodín, coincide con cualquier cosa. Pero puedes cambiarlo para que coincida con una expresión regular personalizada. Dentro de las llaves, justo después del nombre, añade un <
, luego >
y, entre medias, \d+
. Es una expresión regular que significa "un dígito de cualquier longitud".
... lines 3 - 9 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id<\d+>}', methods: ['GET']) ( | |
public function getSong(int $id): Response | |
{ | |
... lines 15 - 22 | |
} | |
} |
¡Pruébalo! Actualiza y... ¡sí! A 404. No se ha encontrado ninguna ruta: simplemente no ha coincidido con esta ruta. Un 404 está muy bien... pero un error 500... eso es algo que queremos evitar. Y si volvemos a /api/songs/5
... eso sigue funcionando.
A continuación: si me preguntaras cuál es la parte más central e importante de Symfony, no lo dudaría: son los servicios. Descubramos qué es un servicio y cómo es la clave para liberar el potencial de Symfony.
Hey @Brentspine ,
Yes, you can customize error pages in Symfony by overriding default templates :) Take a look at this docs page: https://symfony.com/doc/current/controller/error_pages.html
Well, you can't pass vars to that template directly, but you can create a custom Twig function for example that will return you some data you need and call it inside that template :)
Cheers!
// composer.json
{
"require": {
"php": ">=8.0.2",
"ext-ctype": "*",
"ext-iconv": "*",
"symfony/asset": "6.0.*", // v6.0.3
"symfony/console": "6.0.*", // v6.0.3
"symfony/dotenv": "6.0.*", // v6.0.3
"symfony/flex": "^2", // v2.1.5
"symfony/framework-bundle": "6.0.*", // v6.0.4
"symfony/monolog-bundle": "^3.0", // v3.7.1
"symfony/runtime": "6.0.*", // v6.0.3
"symfony/twig-bundle": "6.0.*", // v6.0.3
"symfony/ux-turbo": "^2.0", // v2.0.1
"symfony/webpack-encore-bundle": "^1.13", // v1.13.2
"symfony/yaml": "6.0.*", // v6.0.3
"twig/extra-bundle": "^2.12|^3.0", // v3.3.8
"twig/twig": "^2.12|^3.0" // v3.3.8
},
"require-dev": {
"symfony/debug-bundle": "6.0.*", // v6.0.3
"symfony/stopwatch": "6.0.*", // v6.0.3
"symfony/web-profiler-bundle": "6.0.*" // v6.0.3
}
}
Is it possible to show a standard custom error page for these types of errors? Like when someone requests
api/songs/a
, that I can use the Smart route like shown in the video while explaining what is wrong with the request with a special page instead of showing a simple 404? If not directly, then can I somehow pass arguments to my 404 page for prod?