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 }
Related
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.
I have a Symfony application app and running, I want to run the FOSRestBundle on a prefix only /api/*
I did some research and found one answer that mentioned using zone
zone:
- { path: ^/api/* }
But I'm getting the following error:
Unable to find template "" (looked into......
This is my config:
fos_rest:
format_listener: false
zone:
- { path: ^/api/* }
view:
view_response_listener: 'force'
formats:
json: true
And this is the controller:
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use FOS\RestBundle\View\View;
class APIRestController extends FOSRestController
{
/**
* #Rest\Get("api/gettime")
*/
public function getTimeAction()
{
$restresult = new \DateTime();
$view = View::create($restresult, 200);
return $view;
}
Also:
"friendsofsymfony/rest-bundle": "2.0.0",
"jms/serializer-bundle": "^2.0"
I spent hours reading the documentation and looking online but with no luck, I just keep going in circles!
Fix your configuration:
fos_rest:
format_listener:
rules:
- { path: '^/api', priorities: [ 'json'], fallback_format: json, prefer_extension: true }
zone:
- { path: ^/api/* }
view:
view_response_listener: 'force'
formats:
json: true
I've made a Bundle and a REST controller inside. The "index" method return array in JSON-format, it's ok:
MyBundle/Controller/Api/Rest/BaconController.php
class BaconController extends Controller implements ClassResourceInterface
{
/**
* #var Request $request
* #return array
* #Rest\View
*/
public function cgetAction(Request $request)
{
$mediaType = $request->attributes->get('media_type');
$format = $request->getFormat($mediaType);
my_dump($format);
return array(
array("id" => 1, "title" => "hello",),
array("id" => 2, "title" => "there",),
);
}
}
MyBundle/Resources/config/api/routing_rest.yml
my_api_rest_bacon:
type: rest
resource: "MyBundle:Api/Rest/Bacon"
name_prefix: api_rest_bacon_
prefix: /my/bacon
So, at this point JSON results get returned perfectly:
mysite.com/app_dev.php/api/my/bacon/bacons.json
returns my array.
But now I need to get my controller generate a PDF with the data. So I want it to return PDF document when I call:
mysite.com/app_dev.php/api/my/bacon/bacons.pdf
I've found some half-manuals: RSS view handler, RSS config.ynal, CSV issue with answers. And tried to make something similar:
I've added these lines to
Symfony/app/config/config.yml
framework:
[...some old stuff here...]
request:
formats:
pdf: 'application/pdf'
fos_rest:
body_converter:
enabled: true
format_listener:
rules:
# Prototype array
-
# URL path info
path: ~
# URL host name
host: ~
prefer_extension: true
fallback_format: html
priorities: [html,json]
-
path: ~
host: ~
prefer_extension: true
fallback_format: pdf
priorities: [pdf]
view:
# #View or #Template
view_response_listener: force #true
formats:
json: true
pdf: true
xls: true
html: false
templating_formats:
pdf: false
xls: false
mime_types: {'pdf': ['application/pdf']}
routing_loader:
default_format: html
param_fetcher_listener: true
body_listener: true
allowed_methods_listener: true
services:
my.view_handler.pdf:
class: Lobster\MyBundle\View\PdfViewHandler
my.view_handler:
parent: fos_rest.view_handler.default
calls:
- ['registerHandler', [ 'pdf', [#my.view_handler.pdf, 'createResponse'] ] ]
MyBundle/View/PdfViewHandler.php
namespace Lobster\MyBundle\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)
{
my_dump('pdf createResponse started');
$pdf = "some pdf";
return new Response($pdf, 200, $view->getHeaders());
}
}
So now when I call
mysite.com/app_dev.php/api/my/bacon/bacons.pdf
I see an error An Exception was thrown while handling: Format html not supported, handler must be implemented and my function my_dump saves to a text file info about file format: it is html, not pdf.
Also pdf createResponse didn't work. Why?
So I've found the solution (I will describe how to enable 2 output formats: PDF and XLS):
1) This section in config.yml is not needed:
framework:
[...some old stuff here...]
request:
formats:
pdf: 'application/pdf'
2) fos_rest.format_listener section in config.yml should look like this:
format_listener:
rules:
-
path: '^/api/my/bacon.*\.xls$'
host: ~
prefer_extension: false
fallback_format: json
priorities: [xls, json]
-
path: '^/api/my/bacon.*\.pdf$'
host: ~
prefer_extension: false
fallback_format: json
priorities: [pdf, json]
-
path: ~
host: ~
prefer_extension: true
fallback_format: html
priorities: [html,json]
3) need to add service section into fos_rest in config.yml
fos_rest:
[...]
service:
view_handler: my.view_handler
4) services root section in config.yml should look like
services:
my.view_handler.xls:
class: Lobster\MyBundle\View\XlsViewHandler
my.view_handler.pdf:
class: Lobster\MyBundle\View\PdfViewHandler
my.view_handler:
parent: fos_rest.view_handler.default
calls:
- ['registerHandler', ['xls', [#my.view_handler.xls, 'createResponse'] ] ]
- ['registerHandler', ['pdf', [#my.view_handler.pdf, 'createResponse'] ] ]
And this is it. Now it works perfect
If files will have different data content, then Controller could as well generate file on their own, returning results in BinaryFileResponse.
No need to change any configuration
_format can be used to pick desired file format
All code reside inside controller (and some services related to particular format gen), so adding new stuff or changing existing require changes in small number of files.
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
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