FOSRestBundle设置返回JSON但仍然要求Twig模板

时间:2015-05-08 01:03:15

标签: php json symfony fosrestbundle symfony-2.6

我已将FOSRestBundle配置如下:

#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

我在控制器处有这个:

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);
    }
}

但是当我尝试以下网址app_dev.php/api/v1/reps时,我收到了此错误:

  

无法找到模板“”。 500内部服务器错误 -   InvalidArgumentException 3链接的异常:Twig_Error_Loader»   InvalidArgumentException»InvalidArgumentException»

我希望API返回格式良好的JSON,如下例所示:

{
   "id":"30000001",
   "veeva_rep_id":"0055648764067SwzAAE",
   "display_name":"John Know",
   "avatar_url":"http://freelanceme.net/Images/default%20profile%20picture.png",
   "rep_type":"VEEVA",
   "username":"john@mail.com",
   "first":"John",
   "last":"Know",
   "title":"Sales Representative",
   "phone":"800-555-1212",
   "email":"john@mail.com",
   "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"
}

是不是为返回JSON配置了FOSRestBundle?为什么还要求Twig模板?我该如何解决这个问题?

第一次测试:

@Jeet建议我尝试使用Postman(与他告诉我的扩展名相同),并在将标题Content-Type设置为application/json之后,错误变为此

  

格式错误的JSON

所以,FOSRestBundle没有按原样设置头文件,而控制器没有返回有效的JSON,我该如何解决这些问题呢?

第二次测试:

根据 @Jeet 的建议,我运行此测试:

/**
 * 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);
}

仍然是同一个问题:

  

无法找到模板“”。 500内部服务器错误 -   InvalidArgumentException 3链接的异常:Twig_Error_Loader»   InvalidArgumentException»InvalidArgumentException»

这里还有什么不对?我在配置上遗漏了什么吗?

我最初忘了添加app/config/routing.ymlsrc/PDI/PDOneBundle/Resources/config/routing.yml,所以这里就是这样,也许这是拼图中缺失的部分,让您更好地了解问题的来源:

#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:   /

第三次测试:

客户端的请求肯定有问题,如果我使用像Postman这样的工具并设置正确的标题,我可以按照我的意愿获得实体,请参见下图:

enter image description here

我无法找到问题所在,所以我迫切需要某人的帮助,因为我已经没有想法了

4 个答案:

答案 0 :(得分:24)

正如大家建议的那样:只有Accept标头或扩展名可以给你一个JSON。好像你已经使用Accept标头对它进行了排序。

为了使用扩展,您必须告诉您如何在Symfony中设置格式化内容。

此代码应该为您提供所需的输出:

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:如果您不想使用View类,而是使用纯数组:不要忘记禁止查看SensioExtraBundle的处理

sensio_framework_extra:
    view:    { annotations: false }

Edit2:如果您不使用HTML格式并且只想拥有json输出,则可以使用这样的配置:

fos_rest:
    # ....
    format_listener:
        rules:
            - { path: ^/, priorities: [ json ], fallback_format: json, prefer_extension: true }
    # ....

解释为什么你看到错误“未找到视图”:

TL; DR:您的浏览器发送一个Accept标头,告诉FOSRestBundle输出'html'变体。

背景:此捆绑包主要使用Accept标头,这是一个很好的做法,可以使用所有可用的输出格式:html(您可以使用您提供的表单,对象列表,对象详细信息以这种方式测试您的REST API ),json,xml。有时甚至图像mime类型,如image / jpeg,image / png作为默认值或json / xml作为变体(您可以使用base64图像表示)。

说明:如果您打开浏览器的“网络”标签并查看它发送的标题,您会注意到:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8这意味着“按此顺序使用”:

  1. text / html
  2. application / xhtml + xml
  3. application / xml ,优先级 0.9 根据您的配置禁止
  4. * / * ,优先级 0.8 以及任何格式
  5. 如果您仔细观察,您会看到根据您的配置 text / html 是您的配置('html')和 * / * 是另一个('json'),但 text / html 的优先级为1,而 * / * 的优先级为0.8,因此 text / html 匹配,FOSRestBundle尝试查找HTML表示并失败。

    PS:如果您多次发帖 - 请务必留意每个帖子中的所有回复。

答案 1 :(得分:2)

您可以通过两种方式提供回复

return View::create($entities, Codes::HTTP_OK);

$view = $this->view($entities, Codes::HTTP_OK);    
return $this->handleView($view)

答案 2 :(得分:1)

FosRestBundle利用Accept Header。这意味着它会根据您的请求返回响应。通过访问路线&#34; app_dev.php / api / v1 / reps&#34;,您隐式请求html格式,因此它会尝试提供模板。

app_dev.php / api / v1 / reps.json会返回您需要的内容吗?

您还应该测试app_dev.php / api / v1 / reps.xml并期望xml输出

答案 3 :(得分:1)

您可以简单地使用

            $view = new View($form);
            $view->setFormat('json');
            return $this->handleView($view);