gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Inside our JavaScript, we're making a POST request to the endpoint. And that makes sense. The topic of "which HTTP method" - like GET, POST, PUT, etc - you're supposed to use for an API endpoint... can get complicated. But because our endpoint will eventually change something in the database, as a best-practice, we don't want to allow people to make a GET
request to it. Right now, we can make a GET request by just putting the URL in our browser. Hey! I just voted!
To tighten this up, in CommentController
, we can make our route smarter: we can tell it to only match if the method is POST. To do that add methods="POST"
.
... lines 1 - 8 | |
class CommentController extends AbstractController | |
{ | |
/** | |
* @Route("/comments/{id}/vote/{direction}", methods="POST") | |
*/ | |
public function commentVote($id, $direction) | |
{ | |
... lines 16 - 25 | |
} | |
} |
As soon as we do that, when we refresh... 404 not found! The route no longer matches.
Tip
Actually, it's a 405 response code! HTTP Method Not Allowed.
Another cool way to see this is at your terminal. Run: php bin/console router:match
. Then go copy the URL... and paste it.
php bin/console router:match /comments/10/vote/up
This fun command tells us which route matches a given URL. In this case, no routes match, but it tells us that it almost matched the app_comment_commentvote
route.
To see if a POST
request would match this route, pass --method=POST
:
php bin/console router:match /comments/10/vote/up --method=POST
And... boom! It shows us the route that matched and ALL its details, including the controller.
But there's something else that's not quite right with our route. We're expecting that the {direction}
part will either be up
or down
. But... technically, somebody could put banana
in the URL. In fact, let's try that: change the direction to banana
:
php bin/console router:match /comments/10/vote/banana --method=POST
Yes! We vote "banana" for this comment! This isn't the end of the world... if a bad user tried to hack our system and did this, it would just be a down vote. But we can make this better.
As you know, normally a wildcard matches anything. However, if you want, you can control that with a regular expression. Inside the {}
, but after the name, add <>
. Inside, say up|down
.
... lines 1 - 8 | |
class CommentController extends AbstractController | |
{ | |
/** | |
* @Route("/comments/{id}/vote/{direction<up|down>}", methods="POST") | |
*/ | |
public function commentVote($id, $direction) | |
{ | |
... lines 16 - 25 | |
} | |
} |
Now try the router:match
command:
php bin/console router:match /comments/10/vote/banana --method=POST
Yes! It does not match because banana
is not up or down. If we change this to up
, it works:
php bin/console router:match /comments/10/vote/up --method=POST
By the way, you might be tempted to also make the {id}
wildcard smarter. Assuming we're using auto-increment database ids, we know that id
should be an integer. To make this route only match if the id
part is a number, you an add <\d+>
, which means: match a "digit" of any length.
... lines 1 - 8 | |
class CommentController extends AbstractController | |
{ | |
/** | |
* @Route("/comments/{id<\d+>}/vote/{direction<up|down>}", methods="POST") | |
*/ | |
public function commentVote($id, $direction) | |
{ | |
... lines 16 - 25 | |
} | |
} |
But... I'm actually not going to put that here. Why? Eventually, we're going to use $id
to query the database. If somebody puts banana
here, who cares? The query won't find any comment with an id of banana
and we will add some code to return a 404 page. Even if somebody tries an SQL injection attack, as you'll learn later in our database tutorial, it will still be ok, because the database layer protects against this.
... lines 1 - 8 | |
class CommentController extends AbstractController | |
{ | |
/** | |
* @Route("/comments/{id}/vote/{direction<up|down>}", methods="POST") | |
*/ | |
public function commentVote($id, $direction) | |
{ | |
... lines 16 - 25 | |
} | |
} |
Let's make sure everything still works. I'll close one browser tab and refresh the show page. Yea! Voting still looks good.
Next, let's get a sneak peek into the most fundamental part of Symfony: services.
// composer.json
{
"require": {
"php": "^7.3.0 || ^8.0.0",
"ext-ctype": "*",
"ext-iconv": "*",
"easycorp/easy-log-handler": "^1.0.7", // v1.0.9
"sensio/framework-extra-bundle": "^5.5", // v5.5.3
"symfony/asset": "5.0.*", // v5.0.11
"symfony/console": "5.0.*", // v5.0.11
"symfony/debug-bundle": "5.0.*", // v5.0.11
"symfony/dotenv": "5.0.*", // v5.0.11
"symfony/flex": "^1.3.1", // v1.12.2
"symfony/framework-bundle": "5.0.*", // v5.0.11
"symfony/monolog-bundle": "^3.0", // v3.5.0
"symfony/profiler-pack": "*", // v1.0.5
"symfony/twig-pack": "^1.0", // v1.0.1
"symfony/var-dumper": "5.0.*", // v5.0.11
"symfony/webpack-encore-bundle": "^1.7", // v1.8.0
"symfony/yaml": "5.0.*" // v5.0.11
},
"require-dev": {
"symfony/profiler-pack": "^1.0" // v1.0.5
}
}