FOSRestBundle и JMS Serializer, ошибка при получении JSON

Я пробовал Symfony 2.2, пакет FOSRest (с использованием JMS Serializer) и Doctrine ODM с использованием MongoDB.

После многих часов попыток выяснить, как правильно настроить пакет FOSRest, у меня все еще есть некоторые проблемы: у меня есть очень простой маршрут, который возвращает список продуктов и цен. Всякий раз, когда я запрашиваю формат HTML, я получаю правильный ответ, но если я запрашиваю любой другой формат (JSON, XML), я получаю сообщение об ошибке:

[{"message": "Resources are not supported in serialized data. Path: Monolog\\Handler\\StreamHandler -> Symfony\\Bridge\\Monolog\\Logger -> Doctrine\\Bundle\\MongoDBBundle\\Logger\\Logger -> Doctrine\\Bundle\\MongoDBBundle\\Logger\\AggregateLogger -> Doctrine\\ODM\\MongoDB\\Configuration -> Doctrine\\MongoDB\\Connection -> Doctrine\\ODM\\MongoDB\\LoggableCursor",
    "class": "JMS\\Serializer\\Exception\\RuntimeException",...

вы можете увидеть полное сообщение об ошибке здесь

Моя текущая настройка очень проста: я создал единственный маршрут к контроллеру, который возвращает список продуктов и цену (я следовал этот пример для создания документа о продукте).

Это маршрут:

rest_product:
    type: rest
    resource: Onema\RestApiBundle\Controller\ProductController

Это контроллер:

<?php
namespace Onema\RestApiBundle\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\Rest\Util\Codes;
use JMS\Serializer\SerializationContext;
use Onema\RestApiBundle\Document\Product;

class ProductController extends FOSRestController implements ClassResourceInterface
{
    public function getAction()
    {
        $dm = $this->get('doctrine_mongodb')->getManager();
        $products = $dm->getRepository('RestApiBundle:Product')->findAll();

        if(!$products)
        {
            throw $this->createNotFoundException('No product found.');
        }

        $data = array('documents' => $products);         
        $view = $this->view($data, 200);
        $view->setTemplate("RestApiBundle:Product:get.html.twig");
        return $this->handleView($view);
    }
}

Это представление вызывается из контроллера Resources/Product/get.html.twig:

<ul>
{% for document in documents %}
<li>
    {{ document.name }}<br />
    {{ document.price }}
</li>
{% endfor %}
</ul>

Есть идеи, почему это будет работать правильно для одного формата, но не для других? Что-то еще нужно настроить?

ОБНОВЛЕНИЕ: это значения конфигурации, которые я использовал. В конце app/config/config.yml у меня было это:

sensio_framework_extra:
    view:    { annotations: false }
    router:  { annotations: true }

fos_rest:
    param_fetcher_listener: true
    body_listener: true
    format_listener: true
    view:
        formats:
            json: true
        failed_validation: HTTP_BAD_REQUEST
        default_engine: twig
        view_response_listener: 'force'

ВРЕМЕННОЕ РЕШЕНИЕ:

Проведя немного больше исследований, я столкнулся с другой ошибкой, которая привела меня к этим вопросам и ответам:

https://stackoverflow.com/a/14030646/155248

Однажды я избавился от Doctrine\ODM\MongoDB\LoggableCursor, добавив каждый результат в такой массив:

$productsQ = $dm->getRepository('RestApiBundle:Product')->findAll();

foreach ($productsQ as $product) {
    $products[] = $product;
}

return $products;

Я начал получать результаты в правильном формате. Это своего рода хромое решение, и я все еще надеюсь найти лучший ответ на этот вопрос.


person Onema    schedule 23.04.2013    source источник
comment
Нет необходимости в цикле. Методы Doctrine ODM find* возвращают Cursor. Вы можете позвонить toArray по номеру Cursor.   -  person Entea    schedule 27.06.2013


Ответы (2)


Если вы хотите получить набор документов RestApiBundle:Product, вы ДОЛЖНЫ вызвать метод «toArray» после вызова метода find из репозитория или метода getQuery из построителя запросов.

/**
 * @Route("/products.{_format}", defaults={"_format" = "json"})
 * @REST\View()
 */
public function getProductsAction($_format){
    $products = $this->get('doctrine_mongodb')->getManager()
        ->getRepository('RestApiBundle:Product')
        ->findAll()->toArray();

    return $products;
}

также вы можете вызвать array_values($products) для правильной сериализации стратегии исключения

person BattleBit    schedule 16.10.2013

Скорее всего ошибка кроется где-то в ваших конфигурационных файлах или, может быть, в их недостатке? Добавьте свои конфиги, и если я смогу, я обновлю свой ответ.

А пока я покажу вам простую реализацию.

Для начала начнем с конфигов:

Примечание. Я собираюсь использовать аннотации для некоторых настроек, см. SensioFrameworkExtraBundle.

#app/config/config.yml
sensio_framework_extra:
    view:
        annotations: false

fos_rest:
    param_fetcher_listener: true
    body_listener: true
    format_listener: true
    view:
        view_response_listener: 'force'

Сначала мы устанавливаем дополнительный пакет sensio. конфигурация по умолчанию позволяет установить для аннотаций значение true. Я отключил аннотации для представления (здесь я их использовать не буду).

Для fos_rest мы настраиваем Слушатели, мы не усложняем задачу, поэтому воспользуемся примером из их документации.

Мы создадим сущность:

<?php

namespace Demo\DataBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints;
use JMS\Serializer\Annotation\ExclusionPolicy;  //Ver 0.11+ the namespace has changed from JMS\SerializerBundle\* to JMS\Serializer\*
use JMS\Serializer\Annotation\Expose;  //Ver 0.11+ the namespace has changed from JMS\SerializerBundle\* to JMS\Serializer\*

/**
 * Demo\DataBundle\Entity\Attributes
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Demo\DataBundle\Entity\AttributesRepository")
 * 
 * @ExclusionPolicy("all")
 */
class Attributes
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @Expose
     */
    private $id;

    /**
     * @var string $attributeName
     *
     * @ORM\Column(name="attribute_name", type="string", length=255)
     * 
     * @Expose
     */
    private $attributeName;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set attributeName
     *
     * @param string $attributeName
     * @return Attributes
     */
    public function setAttributeName($attributeName)
    {
        $this->attributeName = $attributeName;

        return $this;
    }

    /**
     * Get attributeName
     *
     * @return string 
     */
    public function getAttributeName()
    {
        return $this->attributeName;
    }
}

Вы заметите пару настроек аннотаций. Сначала мы устанавливаем @ExclusionPolicy("all"), затем вручную устанавливаем, какой объект мы хотим @Expose для API. Вы можете узнать больше об этом здесь и список Аннотации сериализатора JMS

Теперь давайте перейдем к простому контроллеру:

<?php

namespace Demo\DataBundle\Controller;

use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;   //Lets use annotations for our FOSRest config
use FOS\RestBundle\Routing\ClassResourceInterface;   
use FOS\Rest\Util\Codes;
use Symfony\Component\HttpFoundation\Request;
use Demo\DataBundle\Entity\Attributes;


class AttributesController extends FOSRestController implements ClassResourceInterface
{
    /**
     * Collection get action
     * @var Request $request
     * @return array
     *
     * @Rest\View()
     */
    public function cgetAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();

        $entities = $em->getRepository('DemoDataBundle:Attributes')->findAll();

        return array(
                'entities' => $entities,
        );
    }
}

Это простой контроллер, который вернет все.

Надеюсь, это помогло. Я думаю, что ваша ошибка связана с плохой настройкой, связанной с сериализатором. Убедитесь, что вы предоставляете некоторые данные.

person Kirill Fuchs    schedule 26.04.2013
comment
Большое спасибо за ваш подробный ответ, у меня была очень похожая установка, чем та, которую вы описали здесь. Хотя одно различие между вашим и моим заключается в том, что я использовал Doctrine ODM с MongoDB. Я попытался сделать то же самое с сущностью ORM Doctrine, и это сработало сразу. Может ли это быть ошибкой в ​​сериализаторе? Одна забавная вещь, которую я обнаружил с вашей настройкой и ODM, заключается в том, что формат HTML перестает работать, я получаю «Невозможно найти шаблон». ' ЧТО??? Для json и xml я все еще получаю те же сообщения об ошибках, что и раньше. - person Onema; 28.04.2013
comment
На самом деле я принимаю комментарий о том, что формат HTML не работает... Я пропустил аннотацию @Rest\View() в контроллере: P все остальное остается прежним. - person Onema; 28.04.2013