gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Validation for newAction()
, check! Now let's repeat for updateAction
. And that's not much work - we just need to add the whole if (!$form->isValid())
block. I know you hate duplication, so copy the inside of that if
statement, head to the bottom of the class, and add a new private function createValidationErrorResponse()
. We'll pass it the $form
object, and we should type-hint that argument with FormInterface
because we're good programmers! Paste the stuff here:
... lines 1 - 10 | |
use Symfony\Component\Form\FormInterface; | |
... lines 12 - 15 | |
class ProgrammerController extends BaseController | |
{ | |
... lines 18 - 165 | |
private function createValidationErrorResponse(FormInterface $form) | |
{ | |
$errors = $this->getErrorsFromForm($form); | |
$data = [ | |
'type' => 'validation_error', | |
'title' => 'There was a validation error', | |
'errors' => $errors | |
]; | |
return new JsonResponse($data, 400); | |
} | |
} |
Cool! Any time we have a form, we can pass it here and get back a perfectly consistent validation error response. Go back up to newAction()
and use this: return $this->createValidationErrorResponse()
and pass it the $form
object:
... lines 1 - 15 | |
class ProgrammerController extends BaseController | |
{ | |
... lines 18 - 21 | |
public function newAction(Request $request) | |
{ | |
... lines 24 - 27 | |
if (!$form->isValid()) { | |
return $this->createValidationErrorResponse($form); | |
} | |
... lines 31 - 45 | |
} | |
... lines 47 - 177 | |
} |
Copy those three lines and repeat in updateAction()
:
... lines 1 - 15 | |
class ProgrammerController extends BaseController | |
{ | |
... lines 18 - 88 | |
public function updateAction($nickname, Request $request) | |
{ | |
... lines 91 - 101 | |
$form = $this->createForm(new UpdateProgrammerType(), $programmer); | |
$this->processForm($request, $form); | |
if (!$form->isValid()) { | |
return $this->createValidationErrorResponse($form); | |
} | |
... lines 108 - 115 | |
} | |
... lines 117 - 177 | |
} |
We could write a test for this, but we've centralized everything so well, that I'm confident that if it works in newAction
, it works in updateAction()
. Basically, I think that's overkill. But we should re-run our test:
bin/phpunit -c app --filter testValidationErrors
All good. Now run all the tests:
bin/phpunit -c app
Oh! They break immediately! The POST is failing with a 400 response: invalid CSRF token - we saw this a few minutes ago. Every endpoint is failing because we're never sending a CSRF token.
Symfony forms always expect a token. But because we're building a stateless, or session-less API, we don't need CSRF tokens. You would need it if you have a JavaScript frontend that's relying on cookies to authenticate, but you don't need it if your API doesn't store the user in the session.
Let's turn it off. Inside ProgrammerType
, in setDefaultOptions()
- or configureOptions()
if you're on a newer version of Symfony - set csrf_protection
to false:
... lines 1 - 8 | |
class ProgrammerType extends AbstractType | |
{ | |
... lines 11 - 34 | |
public function setDefaultOptions(OptionsResolverInterface $resolver) | |
{ | |
$resolver->setDefaults(array( | |
... lines 38 - 39 | |
'csrf_protection' => false, | |
)); | |
} | |
... lines 43 - 47 | |
} |
That'll do it! Try the tests:
bin/phpunit -c app
Back to green! If you're using your form types for HTML pages and on your API, you won't want to set csrf_protection
to false inside the class - that'll remove it everywhere. Instead, you can pass csrf_protection
in as an option in the third argument to createForm()
in your controller. Or you can do something fancier like a Form Type Extension and control this option on a global basis.
FOSRestBundle has an interesting version of this. In the View Layer part of their docs, they show a configuration option that disables CSRF protection based on a role the user has. The idea is that only users that are authenticated via the sessionless-API would have the role you put here. That's a cool idea.
// composer.json
{
"require": {
"php": ">=5.3.3",
"symfony/symfony": "2.6.*", // v2.6.11
"doctrine/orm": "~2.2,>=2.2.3,<2.5", // v2.4.7
"doctrine/dbal": "<2.5", // v2.4.4
"doctrine/doctrine-bundle": "~1.2", // v1.4.0
"twig/extensions": "~1.0", // v1.2.0
"symfony/assetic-bundle": "~2.3", // v2.6.1
"symfony/swiftmailer-bundle": "~2.3", // v2.3.8
"symfony/monolog-bundle": "~2.4", // v2.7.1
"sensio/distribution-bundle": "~3.0,>=3.0.12", // v3.0.21
"sensio/framework-extra-bundle": "~3.0,>=3.0.2", // v3.0.7
"incenteev/composer-parameter-handler": "~2.0", // v2.1.0
"hautelook/alice-bundle": "0.2.*", // 0.2
"jms/serializer-bundle": "0.13.*" // 0.13.0
},
"require-dev": {
"sensio/generator-bundle": "~2.3", // v2.5.3
"behat/behat": "~3.0", // v3.0.15
"behat/mink-extension": "~2.0.1", // v2.0.1
"behat/mink-goutte-driver": "~1.1.0", // v1.1.0
"behat/mink-selenium2-driver": "~1.2.0", // v1.2.0
"phpunit/phpunit": "~4.6.0" // 4.6.4
}
}
Never mind. I'm an idiot.