FOSRestBundle setup for return JSON but still asking for Twig template
Solution 1
As guys suggested: only Accept header or extension could give you a JSON. Seems like you've got this sorted with Accept header.
In order to use extension you must tell how do you want to set format things in Symfony.
This code should give you an output you want:
namespace RestTestBundle\Controller;
use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Controller\Annotations\Get;
class YourController
{
/**
* @Get("/api/v1/reps.{_format}", defaults={"_format"="json"})
* @View()
*/
public function indexAction()
{
return array(
'status' => 'ok',
'companies' => array(
array('id' => 5),
array('id' => 7),
),
);
}
}
Edit1: if you do not want to use a View
class, but pure arrays: do not forget to disallow View handling of SensioExtraBundle
sensio_framework_extra:
view: { annotations: false }
Edit2: If you do not use HTML format and only want to have a json output you can use such fonfiguration:
fos_rest:
# ....
format_listener:
rules:
- { path: ^/, priorities: [ json ], fallback_format: json, prefer_extension: true }
# ....
Explanation why you do see an error "view not found":
TL;DR: your browser send an Accept header that tells FOSRestBundle to output a 'html' variant.
Background: This bundle works mostly with Accept headers, it's a good practice to have all possible output formats that are available: html (you can test your REST API with forms you provide, lists of objects, details of objects easily this way), json, xml. Sometimes even image mime types like image/jpeg, image/png as default or json/xml as a variant (you can use base64 image representation).
Explanation: If you open up a "network" tab of a browser and check out headers it sends you will notice something like: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
which means "use in such order":
- text/html
- application/xhtml+xml
- application/xml with priority of 0.9 which is forbidden according to your configuration
- */* with priority 0.8 which meand any format
If you look close to this is you will see that according to your configuration text/html is one of variants that your configuration has ('html') and */* is another one ('json'), but text/html has a priority of 1, while */* has a priority of 0.8, so text/html matches and FOSRestBundle tries to find a HTML representation and fails.
PS: If you post question multiple times - please make sure you watch for all responses in every thread.
Solution 2
You can give response in two ways
return View::create($entities, Codes::HTTP_OK);
or
$view = $this->view($entities, Codes::HTTP_OK);
return $this->handleView($view)
Solution 3
You can use simply
$view = new View($form);
$view->setFormat('json');
return $this->handleView($view);
Solution 4
FosRestBundle leverages the Accept Header. This means that it returns a response based on what you request. By accessing the route "app_dev.php/api/v1/reps", you are implicitly requesting an html format, so it tries to provide a template.
Does app_dev.php/api/v1/reps.json return what you need?
You should also test app_dev.php/api/v1/reps.xml and expect an xml output
ReynierPM
A passionate programmer and web developer with a background in front-end and back-end development, which is what he's been doing for over eight years. I had experience in web development using PHP (mostly), MySQL and JavaScript. I follows two major principles everyday work: beauty and simplicity. I believes everyone should learn something new every day. While I'm not working, I spends time coding personal projects, learning, watching screen casts, blogging, etc. Some specific areas of interest for me include cloud computing and anything related to web development among other like system and database administration.
Updated on June 23, 2022Comments
-
ReynierPM almost 2 years
I have configured FOSRestBundle as following:
#FOSRestBundle fos_rest: param_fetcher_listener: true body_listener: true format_listener: rules: - { path: ^/, priorities: [ json, html ], fallback_format: ~, prefer_extension: true } media_type: version_regex: '/(v|version)=(?P<version>[0-9\.]+)/' body_converter: enabled: true validate: true view: mime_types: json: ['application/json', 'application/json;version=1.0', 'application/json;version=1.1'] view_response_listener: 'force' formats: xml: false json: true templating_formats: html: true exception: codes: 'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404 'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT messages: 'Symfony\Component\Routing\Exception\ResourceNotFoundException': true allowed_methods_listener: true access_denied_listener: json: true
And I have this at controller:
namespace PDI\PDOneBundle\Controller\Rest; use FOS\RestBundle\Controller\FOSRestController; use Nelmio\ApiDocBundle\Annotation\ApiDoc; use FOS\RestBundle\Controller\Annotations\QueryParam; use FOS\RestBundle\Controller\Annotations\Get; class RepresentativeRestController extends FOSRestController { /** * Get all representatives. * * @return array * * @ApiDoc( * resource = true, * https = true, * description = "Get all representatives.", * statusCodes = { * 200 = "Returned when successful", * 400 = "Returned when errors" * } * ) * @Get("/api/v1/reps") */ public function getRepsAction() { $em = $this->getDoctrine()->getManager(); $entities = $em->getRepository('PDOneBundle:Representative')->findAll(); if(!$entities) { return $this->view(null, 400); } return $this->view($entities, 200); } }
But when I try the following URL
app_dev.php/api/v1/reps
I got this error:Unable to find template "". 500 Internal Server Error - InvalidArgumentException 3 linked Exceptions: Twig_Error_Loader » InvalidArgumentException » InvalidArgumentException »
I expect that API return a well formed JSON as the following example:
{ "id":"30000001", "veeva_rep_id":"0055648764067SwzAAE", "display_name":"John Know", "avatar_url":"http://freelanceme.net/Images/default%20profile%20picture.png", "rep_type":"VEEVA", "username":"[email protected]", "first":"John", "last":"Know", "title":"Sales Representative", "phone":"800-555-1212", "email":"[email protected]", "territory_id":"200454001", "inactive":"no", "total_contacts":"6", "total_shares":"0", "totalViews":"0", "lastLoginAt":"2015-05-05 15:45:57", "lastVeevaSyncAt":"2015-05-05 15:45:57", "createdAt":"2015-05-05 15:45:57", "updatedAt":"2015-05-05 15:45:57" }
Is not FOSRestBundle configured for return JSON? Why still asking for Twig template? How can I fix this?
First test:
As @Jeet suggest me I have tried using Postman (is the same as the extension he told me) and after set the header
Content-Type
toapplication/json
the error turns into thisMalformed JSON
so, the FOSRestBundle is not setting up headers as should be and controller is not returning a valid JSON, how do I fix those ones?
Second test:
As suggested by @Jeet I run this test:
/** * Get all representatives. * * @return array * * @ApiDoc( * resource = true, * https = true, * description = "Get all representatives.", * statusCodes = { * 200 = "Returned when successful", * 400 = "Returned when errors" * } * ) * @Get("/api/v1/reps") * @View() */ public function getRepsAction() { $em = $this->getDoctrine()->getManager(); $entities = $em->getRepository('PDOneBundle:Representative')->findAll(); $temp = array("1", "2", "3"); $view = $this->view($temp, Codes::HTTP_OK); return $this->handleView($view); }
And still the same issue:
Unable to find template "". 500 Internal Server Error - InvalidArgumentException 3 linked Exceptions: Twig_Error_Loader » InvalidArgumentException » InvalidArgumentException »
What else can be wrong here? Did I'm missing something at configuration?
I forgot to add
app/config/routing.yml
andsrc/PDI/PDOneBundle/Resources/config/routing.yml
at first so here them goes, perhaps this is the missing piece on the puzzle and give you a better idea of where the problem comes from:#app/config/routing.yml #PDOne pdone: resource: "@PDOneBundle/Resources/config/routing.yml" template: resource: "@TemplateBundle/Resources/config/routing.yml" #FOSUserBundle fos_user: resource: "@FOSUserBundle/Resources/config/routing/all.xml" prefix: / #NelmioApiDocBundle: NelmioApiDocBundle: resource: "@NelmioApiDocBundle/Resources/config/routing.yml" prefix: /api/doc #SonataAdmin admin: resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml' prefix: /admin _sonata_admin: resource: . type: sonata_admin prefix: /admin #src/PDI/PDOneBundle/Resources/config/routing.yml pdone: resource: "@PDOneBundle/Controller/" type: annotation prefix: /
Third test:
Definitely something is wrong with request from client side, if I use a tool like
Postman
and set proper headers I got the entities as I want, see pic below:I can't find where the problem is so I desperately need someone's help here because I was already out of ideas
-
ReynierPM almost 9 yearsNone of those three works for me, when I put
.html
or.json
or.xml
I got 404 route error, I can't find what I'm missing in my config or at controller side -
ReynierPM almost 9 yearsThis way works but only from Postman Chrome extension by setting the right headers for
Content-Type
if I try from browser I got the same issue with templating which makes me ask: why? where is the issue on the configuration and why is not headers setup right? Also I like to return some kind ofNotFoundException
if there is not$entities
, can you show me how to achieve that on your code? -
le0diaz over 8 yearsYour response has the solution but it's implicit in the content. You should specifically describe that for testing with some clients like Chrome/Firefox RestClient extension One HAVE TO set the proper header: Accept:application/json