gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Now that we have a new page, at your terminal, run debug:router
again.
php bin/console debug:router
Yep, there's our new endpoint! Notice that the table has a column called "Method" that says "any". This means that you can make a request to this URL using any HTTP method - like GET or POST - and it will match that route.
But the purpose of our new API endpoint is to allow users to make a GET request to fetch song data. Technically, right now, you could also make a POST for request to this... and it would work just fine. We might not care, but often with APIs, you'll want to restrict an endpoint to only work with one specific method like GET, POST or PUT. Can we make this route somehow only match GET requests?
Yep! By adding another option to the Route
. In this case, it's called methods
, it even auto-completes! Set this to an array and, put GET
.
... lines 3 - 9 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id}', methods: ['GET']) ( | |
public function getSong($id): Response | |
{ | |
... lines 15 - 22 | |
} | |
} |
I'm going to hold Command and click into the Route
class again... so we can see that... yup! methods
is one of the arguments.
Back over on debug:router
:
php bin/console debug:router
Nice. The route will now only match GET requests. It's... kind of hard to test this, since a browser always makes GET requests if you go directly to a URL... but this is where another bin/console
command comes in handy: router:match
.
If we run this with no arguments:
php bin/console router:match
It gives us an error but shows how it's used! Try:
php bin/console router:match /api/songs/11
And... that matches our new route! But now ask what would happen if we made a POST request to that URL with --method=POST
:
php bin/console router:match /api/songs/11 --method=POST
No routes match this path with that method! But it does say that it almost matched our route.
Let's do one more thing to tighten up our new endpoint. I'm going to add an int
type-hint to the $id
argument.
... lines 3 - 9 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id}', methods: ['GET']) ( | |
public function getSong(int $id): Response | |
{ | |
... lines 15 - 22 | |
} | |
} |
That... doesn't change anything, except that PHP will now take the string id
from the URL that Symfony passes into this method and cast it into an int
, which is... just nice because then we're dealing with a true integer in our code.
You can see the subtle difference in the response. Right now, the id
field is a string. When we refresh, id
is now a true number in JSON.
But... if somebody was being tricky... and went to /api/songs/apple
... yikes! A PHP error, which, on production, would be a 500 error page! I do not like that.
But... what can we do? The error comes comes from when Symfony tries to call our controller and passes in that argument. So it's not like we can put code down in the controller to check if $id
is a number: it's too late!
So what if, instead, we could tell Symfony that this route should only match if the id
wildcard is a number. Is that possible? Totally!
By default, when you have a wildcard, it matches anything. But you can change it match a custom regular expression. Inside of the curly braces, right after the name, add a <
then >
and, in between, \d+
. That's a regular expression meaning "a digit of anything length".
... lines 3 - 9 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id<\d+>}', methods: ['GET']) ( | |
public function getSong(int $id): Response | |
{ | |
... lines 15 - 22 | |
} | |
} |
Try it! Refresh and... yes! A 404. No route found: it simply didn't match this route. A 404 is great... but a 500 error... that's something we want to avoid. And if we head back to /api/songs/5
... that still works.
Next: if you asked me what the most central and important part of Symfony is, I wouldn't hesitate: it's services. Let's find out what a service is and how it's the key to unlocking Symfony's potential.
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?