FOSRestBundle returning empty content - php

I'm implementing FOSUserBundle in my project. I want a basic GET action to return a json object of the ContactList entity.
Controller:
class ContactListController extends FOSRestController
{
use ViewContextTrait;
const DEFAULT_GROUPS = ['organization_list'];
/**
* #ParamConverter("contactList", class="SchemaBundle:ContactList")
* #param ContactList $contactList
* #return Response
*/
public function getAction(ContactList $contactList)
{
return $this->handleView($this->viewWithContext($contactList, Response::HTTP_OK));
}
Trait:
use FOS\RestBundle\Context\Context;
use FOS\RestBundle\View\View;
trait ViewContextTrait
{
public function viewWithContext($data, $statusCode = null, $groups = self::DEFAULT_GROUPS)
{
$context = new Context();
$context->setGroups($groups);
return View::create($data, $statusCode)->setContext($context);
}
}
My config.yml:
fos_rest:
routing_loader:
include_format: false
body_listener:
array_normalizer: fos_rest.normalizer.camel_keys
param_fetcher_listener: true
view:
view_response_listener: 'force'
format_listener:
rules:
- { path: '^/api', priorities: ['json'], fallback_format: json, prefer_extension: false }
The problem: When I call this route via postman (/api/contact-list/1), I always get a {} for my content in the Response object.
This is the dumped response:
What am I missing in order to return the serialized ContactList entity w/ the context group in my Response?

Solution:
First of all, my annotations weren't included. I had to add this to my config.yml:
framework:
serializer:
enabled: true
enable_annotations: true
Next, I forgot I had previously included JMS serializer into my project awhile back. Apparently the ViewHandler for the Rest Bundle has a default order of serializer services that it uses. I ended up having to include this in my config.yml:
fos_rest:
service:
serializer: fos_rest.serializer.symfony
By default, FOSRest was using JMS Serializer which wasn't configured properly.

Related

SensioFrameworkExtraBundle

I'm using Symfony 3.3 and i'm trying to use FOSRestController to make an API.
Here is my config files :
# SensioFrameworkExtra Configuration
sensio_framework_extra:
view: { annotations: false }
# FOSRest Configuration
fos_rest:
format_listener:
rules:
- { path: '^/api', priorities: ['json'], fallback_format: 'json' }
- { path: '^/', stop: true }
view:
view_response_listener: true
Controller :
<?php
namespace AppBundle\Api;
use FOS\RestBundle\Controller\Annotations as REST;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
class MyController extends FOSRestController
{
/**
* #REST\Get("/some-url")
* #REST\View()
*
* #return View
*/
public function getSomethingAction()
{
$view = View::create();
return $view;
}
}
The issue is about the view_response_listener, i'm having this error message :
(1/1) RuntimeException
You must enable the SensioFrameworkExtraBundle view annotations to use the ViewResponseListener.
Routing :
api_something:
type: rest
resource: AppBundle\Api\MyController
The bundle is already installed and added to the AppKernel.php file
Can something help me with this ?
Thanks
Remove your configuration of sensio_framework_extra :
sensio_framework_extra:
view: { annotations: false }
Because the default configuration is annotations: true (you can look at vendor/sensio/framework-extra-bundle/DependencyInjection/Configuration.php)
Let the default configuration of sensio_framework_extra https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html#configuration
You may have forgotten to activate the annotations of serializer in config.yml (https://symfony.com/doc/current/serializer.html#using-serialization-groups-annotations)
I suggest you to try this configs :
#app/config/config.yml
framework:
....
serializer: { enable_annotations: true }
fos_rest:
....
view:
view_response_listener: 'force'
Documentation of FosRestBundle view_response_listener :
http://symfony.com/doc/master/bundles/FOSRestBundle/3-listener-support.html
http://symfony.com/doc/master/bundles/FOSRestBundle/view_response_listener.html
Try to create the directory AppBundle/Api/Controller. And put your MyController.php into it. Your controller will be named
\AppBundle\Api\Controller\MyController
Copy the following code to your main services.yml
sensio_framework_extra.view.listener:
alias: Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener
Source of the solution:
https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1768#issuecomment-340294485
another solution from mentioned source:
in bundles.php the Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle must be located after FOS\RestBundle\FOSRestBundle to work properly
Answer for the same question with Symfony 4.0
routes/rest.yaml Routing configuration
app_admin_users:
type: rest
resource: App\Controller\Api\Admin\User\UsersController
prefix: /api
fos_rest.yaml view response listener enabled
fos_rest:
view:
view_response_listener: true
framework_extra.yaml view annotations enabled
sensio_framework_extra:
view: { annotations: true }
config.yaml imports fos_rest and framework_extra
imports:
- { resource: fos_rest.yaml }
- { resource: framework.yaml }
- { resource: framework_extra.yaml }

Symfony3 Fos Rest Bundle - download pdf but return json on error

i have a problem with Fos Rest Bundle - everything works fine with jsons (response is always in this format, including errors). Now i'm stuck with PDF download. I have two problems:
I have to send Accept header with "application/pdf" to get pdf file, if i use other type, i'll get an error, that it can't be serialized to json.
Uncaught PHP Exception RuntimeException: "Your data could not be encoded because it contains invalid UTF8 characters."
I want to force the endpoint to always use pdf format, no matter what is in the Accept header.
When there's an error while generating PDF, the response should be in json format, with error details (it works for other endpoints). Right now, errors are returned in html format.
Keep in mind, that i'm using dev environment for now, so i.e. exception part in config.yml is commented out.
My routing:
/doc is nelmio api doc - works fine
/pdf/{id} should accept json and return pdf (with one exception - json on error) - not working as i want
/ (other endpoints) should accept json, and returns json (errors too) - works fine
What i have right now:
config.yml
fos_rest:
access_denied_listener:
html: true
twig: true
body_converter:
enabled: true
view:
default_engine: json
view_response_listener: force
templating_formats:
html: true
twig: false
mime_types:
pdf: ['application/pdf']
formats:
json: true
pdf: false
routing_loader:
default_format: json
include_format: false
format_listener:
rules:
- { path: '^/pdf', priorities: ['pdf'], fallback_format: json, prefer_extension: false }
# /doc is nelmio api doc, so html is correct here
- { path: '^/doc', priorities: ['html'], fallback_format: ~, prefer_extension: false }
- { path: '^/', priorities: ['json'], fallback_format: json, prefer_extension: false }
#exception:
#codes:
# 'ApiBundle\Exception\InvalidArgumentException': 400
# 'ApiBundle\Exception\Exception': true
serializer:
serialize_null: true
service:
view_handler: api.fos_rest.view_handler
services.yml
api.fos_rest.view_handler:
parent: fos_rest.view_handler.default
calls:
- ['registerHandler', ['pdf', ["#api.fos_rest.view_handler.pdf", 'createResponse']]]
api.fos_rest.view_handler.pdf:
class: ApiBundle\View\PdfViewHandler
PdfViewHandler.php
<?php
namespace ApiBundle\View;
use FOS\RestBundle\View\View;
use FOS\RestBundle\View\ViewHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class PdfViewHandler
{
public function createResponse(ViewHandler $handler, View $view, Request $request, $format)
{
return new Response($view->getData(), 200, $view->getHeaders());
}
}
DocumentController.php
<?php
namespace ApiBundle\Controller;
use ApiBundle\Document\FilledSurvey;
use FOS\RestBundle\Controller\Annotations as Rest;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
/**
* Class DocumentController
* #package ApiBundle\Controller
*
* #Rest\Route("/pdf")
*/
class DocumentController extends AbstractController
{
/**
* #param FilledSurvey $filledSurvey
* #return mixed
*
* #ApiDoc(
* section = "Document",
* description = "Get document",
* statusCodes = {
* 200 = "Document returned",
* }
* )
*
* #Rest\Get("/{id}", requirements={"id": "[a-zA-Z0-9]+"}, name="document_get")
* #Rest\View(serializerGroups={"???"})
*/
public function getAction(FilledSurvey $filledSurvey)
{
$path = $this->get('api.pdf.generator')->generate($filledSurvey);
return file_get_contents($path);
}
}

How do routes in FOSRestBundle work?

Can someone clearly explain how routes are supposed to be configured for REST requests using FOSRest? Every tutorial seems to do it differently.
My Controller:
<?php
namespace Data\APIBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DatasetController extends Controller{
protected function postDatasetAction(Request $request){
//Query here
}
The URL should look something like this: Symfony/web/app_dev.php/api/dataset. So I thought the routes should be something like...
app/config/routes.yml
data_api:
resource: "#DataAPIBundle/Resources/config/routing.yml"
prefix: /api
type: rest
And....
Data/APIBundle/Resources/config/routing.yml
data_query:
type: rest
pattern: /dataset
defaults: {_controller: DataAPIBundle:Dataset:datasetAction, _format: json }
requirements:
_method: POST
Please follow the next URL to read the official documentation:
http://symfony.com/doc/master/bundles/FOSRestBundle/index.html
To start with this bundle, I would suggest following the single restful controller documentation:
http://symfony.com/doc/master/bundles/FOSRestBundle/5-automatic-route-generation_single-restful-controller.html
You will also find clear examples (https://github.com/liip/LiipHelloBundle) about what this bundle can offer.
Few things from the snippets you have posted drew my attention:
The visibility of your controller method is protected whereas it should be public (http://symfony.com/doc/current/book/controller.html)
public function postDatasetAction(Request $request) {
// your code
}
The "routing.yml" file created to configure your route shall contain the name of the aforementioned controller method (postDatasetAction instead of DatasetAction):
# routing.yml
data_query:
type: rest
pattern: /dataset
defaults: {_controller: DataAPIBundle:Dataset:postDatasetAction, _format: json }
requirements:
_method: POST
Please find below an example to setup a route like :
get_items GET ANY ANY /items.{json}
# config.yml
fos_rest:
allowed_methods_listener: true
format_listener:
default_priorities: ['json', html, '*/*']
fallback_format: json
prefer_extension: true
param_fetcher_listener: true
routing_loader:
default_format: json
view:
formats:
json: true
mime_types:
json: ['application/json', 'application/x-json']
force_redirects:
html: true
view_response_listener: force
# routing.yml
categories:
type: rest
resource: Acme\DemoBundle\Controller\ItemController
<?php
namespace Acme\DemoBundle\Controller
use FOS\RestBundle\Request\ParamFetcher;
use FOS\RestBundle\Controller\Annotations as Rest;
class ItemController
{
/**
* Get items by constraints
*
* #Rest\QueryParam(name="id", array=true, requirements="\d+", default="-1", description="Identifier")
* #Rest\QueryParam(name="active", requirements="\d?", default="1", description="Active items")
* #Rest\QueryParam(name="from", requirements="\d{4}-\d{2}-\d{2}", default="0000-00-00", description="From date")
* #Rest\QueryParam(name="to", requirements="\d{4}-\d{2}-\d{2}", default="0000-00-00", description="End date")
* #Rest\QueryParam(name="labels", array=true, requirements="\d+", default="-1", description="Labels under which items have been classifed")
*
* #Rest\View()
*
* #param ParamFetcher $paramFetcher
*/
public function getItemsAction(ParamFetcher $paramFetcher) {
$parameters = $paramFetcher->all();
// returns array which will be converted to json contents by FOSRestBundle
return $this->getResource($parameters);
}
}
P.S. : You will need to add a view to display the resource as an HTML page
you are missing the routing part of FOSRestbundle in the controller:
protected function postDatasetAction(Request $request){
//Query here
} // "post_dataset" [POST] /dataset

FOSRestBundle can't get param fetcher

I'm trying to make my controller work with param fetcher. I did all instructions specified in the documentation. So what I have:
config
fos_rest.yml:
fos_rest:
param_fetcher_listener: 'force'
view:
formats:
json: true
xml: false
rss: false
mime_types:
json: ['application/json', 'application/x-json', 'application/vnd.example-com.foo+json']
png: 'image/png'
failed_validation: HTTP_BAD_REQUEST
default_engine: twig
view_response_listener: 'force'
format_listener:
default_priorities:
- json
routing_loader:
default_format: json
sensio_framework_extra:
view: { annotations: false }
router: { annotations: true }
And my controller
<?php
namespace Push\PointsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use FOS\RestBundle\View\View;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Request\ParamFetcherInterface;
use FOS\RestBundle\Controller\Annotations\RequestParam;
use FOS\RestBundle\Request\ParamFetcher;
class RestController extends Controller
{
/**
* #QueryParam(name="latitude", requirements="[0-9.]+", default="0", description="")
* #ApiDoc()
*/
public function checkPointsAction(ParamFetcher $params)
{
$view = new View();
return $this->get('fos_rest.view_handler')->handle($view);
}
}
When I calls method I get:
Controller
"Push\PointsBundle\Controller\RestController::checkPointsAction()"
requires that you provide a value for the "$params" argument (because
there is no default value or because there is a non optional argument
after this one).
What I did wrong or missed something? Thank you.
I often have problems with FOSRestBundle and outdated cache. It simply doesn't refresh sometimes if I make a change in annotations. First, try removing contents of your app/cache directory (rm -rf app/cache/*).
I didn't check if parameters are mapped to method arguments by a type or a name. If it's the later than your parameter should be called $paramFetcher (not $params):
public function checkPointsAction(ParamFetcher $paramFetcher) { }
Edit: have you enabled param fetcher listener?
fos_rest:
param_fetcher_listener: true
I had the same issue, as pointed out in the comment. I solved this by changing the Variable name from
public function addMapAction(ParamFetcher $pf) {
to
public function addMapAction(ParamFetcher $paramFetcher) {
And all the sudden i worked like a charm, apparently $paramFetcher is required.

Cannot import resource error

I just installed FOSRestBundle I´m getting this error when I run cache:clear
[Symfony\Component\Config\Exception\FileLoaderLoadException]
Cannot import resource "/Users/gitek/www/hotel/src/Gitek/RegistroBundle/Resources/config/routing_incidencia.yml" from "/Users/gitek/www/hotel/app/config/routing.yml".
[RuntimeException]
The autoloader expected class "Gitek\RegistroBundle\Controller\IncidenciaController" to be defined in file "/Users/gitek/www/hotel/app/../src/Gitek/RegistroBundle/Controller/IncidenciaController.php". The file was found but the class was not in it, the class name or namespace probably has a typo.
This is my controller:
<?php
namespace Gitek\RegistroBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\View\View;
use Gitek\RegistroBundle\Entity\Registro;
use Gitek\HotelBundle\Entity\Incidencia;
class UsuarioController extends Controller
{
public function putIncidenciaAction($registro_id, $incidencia_id)
{
$em = $this->get('doctrine')->getEntityManager();
$registro = $em->getRepository('RegistroBundle:Registro')->find($registro_id);
$incidencia = $em->getRepository('HotelBundle:Incidencia')->find($incidencia_id);
$request = $this->getRequest();
$registro->setIncidencia($incidencia);
$em->persist($registro);
$em->flush();
$view = View::create();
$view->setData($registro);
return $view;
} // "put_incidencia" [PUT] /incidencia/{registro_id, incidencia_id}
}
This is my #app/config/routing.yml
incidencias:
resource: "#RegistroBundle/Resources/config/routing_incidencia.yml"
prefix: /
type: rest
This is my #src/Gitek/RegistroBundle/Resources/config/routing_incidencia.yml
incidencia:
type: rest
resource: Gitek\RegistroBundle\Controller\IncidenciaController
name_prefix: api_
Finally, this is my config for fos_rest on #app/config.yml:
fos_rest:
routing_loader:
default_format: null
view:
default_engine: twig
force_redirects:
html: true
formats:
json: true
xml: true
templating_formats:
html: true
view_response_listener: 'force'
failed_validation: HTTP_BAD_REQUEST
exception:
codes: ~
messages: ~
body_listener:
decoders:
json: fos_rest.decoder.json
xml: fos_rest.decoder.xml
format_listener:
default_priorities: [json, html, '*/*']
fallback_format: html
prefer_extension: true
service:
router: router
templating: templating
serializer: serializer
view_handler: fos_rest.view_handler.default
Any help or clue??
Your controller class should be named IncidenciaController instead of UsuarioController

Categories