I am developing a website that offers a REST service. All the GET actions are OK and rendered using a .json.twig template, but I am having a hard time understanding how to output form errors if the query made to create a new record is not valid. If I try to do a simple
return $form;
I get the following exception from SF:
"exception":[{"message":"Unable to find template \"SomeBundle:Customers:postCustomer.json.twig\"}]
The template does not exist, that's true, but I have no idea how to create one in JSON format to tell the requestor that his query is incomplete / malformed.
If I try anything else dealing with views but without specifying a template, the result is the same. Is there a way to do that automatically so that if the form is modified the change are reflected as well in the error ? Or a way to tell FOSRestBundle / JMSSerializerBundle to deal with the serialization themselves ? Before switching to Twig responses the error was nicely handled, and I'd like to have that back, along with the Twig templates for normal operations.
For information, my current controller's action is:
/**
* @ApiDoc(
* resource=false,
* input="SomeBundle\Form\CustomerType",
* description="Create a new customer",
* section="Customers",
* statusCode={
* 201="Action successful",
* 403="Authorization required but incorrect / missing information or insufficient rights",
* 500="Returned if action failed for unknown reasons"
* }
* )
*
* --View(template="SomeBundle:Customers:add.json.twig", templateVar="form", statusCode=400)
* @View(templateVar="form", statusCode=400)
* @param Request $request
* @return \FOS\RestBundle\View\View
*/
public function postCustomerAction(Request $request) {
$data = json_decode($request->getContent(), true);
$manager = $this->getManager();
$customer = new Customer();
$form = $this->getForm($customer);
//$form->submit($data);
//$manager->create($customer);
// $form->handleRequest($request);
// if ($form->isSubmitted() && $form->isValid()) {
// $manager->create($customer);
//
// return $this->redirectView($this->generateUrl('api_get_customer_internal', ['uuid' => $customer->getInternalUuid()], true),
// 201);
// }
return $form;
//return $this->handleView($this->view($form, 400));
//return \FOS\RestBundle\View\View::create($form, 400);
}
And the FOSRestBundle configuration:
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener:
enabled: true
view:
view_response_listener: 'force'
formats:
json: true
templating_formats:
json: true
force_redirects:
html: true
failed_validation: HTTP_BAD_REQUEST
default_engine: twig
routing_loader:
include_format: false
default_format: json
serializer:
serialize_null: true
sensio_framework_extra:
view:
annotations: true
Thanks to jorge07 at https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1620 I was able to find a way to circumvent that in a rather proper way (at least IMHO), here's the updated Controller action (no change in the fosrestbundle settings required):
/**
* @Route("/customers")
* @ApiDoc(
* resource=false,
* input="NetDev\CoreBundle\Form\CustomerType",
* description="Create a new customer",
* section="Customers",
* statusCode={
* 201="Action successful",
* 403="Authorization required but incorrect / missing information or insufficient rights",
* 500="Returned if action failed for unknown reasons"
* }
* )
*
* @View(template="NetDevRestBundle:Common:form_error.json.twig", templateVar="errors", statusCode=400)
*
* @RequestParam(name="customerName", nullable=false)
* @RequestParam(name="customerIndex", nullable=false)
*
* @return \FOS\RestBundle\View\View
*/
public function postCustomerAction(ParamFetcher $fetcher)
{
$customer = new Customer();
$form = $this->getForm($customer);
$form->submit($fetcher->all(), true);
if ($form->isValid()) {
$manager = $this->getManager();
$manager->create($customer);
return $this->redirectView($this->generateUrl('api_get_customer_internal', ['uuid' => $customer->getInternalUuid()], true), 201);
}
$err = $form->getErrors();
$errorsList = [];
foreach ($err as $it) {
$errorsList[(string)$it->getOrigin()->getPropertyPath()] = $it->getMessage();
}
return $this->view([$errorsList])
->setTemplateVar('errors')
;
}