Настройка FOSRestBundle для возврата JSON, но по-прежнему запрашивается шаблон Twig

Я настроил 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);
    }
}

Но когда я пытаюсь использовать следующий URL-адрес 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":"[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"
}

Разве FOSRestBundle не настроен для возврата JSON? Почему все еще просите шаблон 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.yml и src/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 и устанавливаю правильные заголовки, я получаю объекты, как я хочу, см. рис. ниже:

введите здесь описание изображения

Я не могу найти, в чем проблема, поэтому мне отчаянно нужна чья-то помощь, потому что у меня уже не было идей.


person ReynierPM    schedule 08.05.2015    source источник
comment
Вы настроили «Content-Type» как «application/json»?   -  person Jeet    schedule 08.05.2015
comment
@Джит, где я должен это сделать? Разве не в конфигурации FOSRes, как вы можете видеть на ОП?   -  person ReynierPM    schedule 08.05.2015
comment
Попробуйте app_dev.php/api/v1/reps в приложении Advanced Rest Client Google Chrome (расширение приложения), там у вас будет возможность.   -  person Jeet    schedule 08.05.2015
comment
@Jeet, посмотри OP, я отредактировал, добавив некоторую информацию   -  person ReynierPM    schedule 08.05.2015
comment
FOSRestBundle отвечает за установку заголовка для ответа, отправляемого клиенту. Но его клиент (мобильное приложение, расширение клиента REST) ​​обязан установить правильные значения заголовка при отправке его на сервер. FOSRestBundle может ответить соответствующим образом. ;)   -  person Jeet    schedule 11.05.2015
comment
@Джит, как это? Я имею в виду, как я могу это исправить? Что клиент должен отправить на сервер, чтобы получить правильный ответ?   -  person ReynierPM    schedule 11.05.2015
comment
Я предполагаю, что вы внесли изменения со стороны клиента. Здесь $entities не удалось нормализовать до Джейсона, поскольку у меня был такой же опыт. Попробуйте отправить простой массив и посмотрите, все ли в порядке с данными JSON.   -  person Jeet    schedule 11.05.2015
comment
@Jeet не работает, смотрите мое редактирование в OP. Я добавил маршруты, возможно, там что-то не так, и я не видел этого раньше.   -  person ReynierPM    schedule 11.05.2015
comment
Вы тестируете путь API в простом веб-браузере, потому что, как я понимаю, это не сработает. Потребитель API должен установить для своего заголовка content-type значение application/json, как это делает ваше расширение Postman.   -  person Jeet    schedule 12.05.2015


Ответы (4)


Как предложили ребята: только заголовок или расширение 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 , хмл. Иногда даже MIME-типы изображений, такие как image/jpeg, image/png по умолчанию или json/xml в качестве варианта (вы можете использовать представление изображения base64).

Объяснение: если вы откроете вкладку «сеть» браузера и проверите заголовки, которые он отправляет, вы заметите что-то вроде: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 что означает «использовать в таком порядке»:

  1. текст/html
  2. приложение/xhtml+xml
  3. application/xml с приоритетом 0,9, что запрещено вашей конфигурацией
  4. */* с приоритетом 0,8, что означает любой формат

Если вы внимательно посмотрите на это, вы увидите, что в соответствии с вашей конфигурацией text/html является одним из вариантов вашей конфигурации ('html'), а */* другой ('json'), но text/html имеет приоритет 1, а */* имеет приоритет 0,8, поэтому text/html< /em> совпадает, а FOSRestBundle пытается найти HTML-представление и терпит неудачу.

PS: Если вы публикуете вопрос несколько раз, убедитесь, что вы следите за всеми ответами в каждой ветке.

person Artem L    schedule 12.05.2015

Вы можете дать ответ двумя способами

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

or

$view = $this->view($entities, Codes::HTTP_OK);    
return $this->handleView($view)
person Farid Movsumov    schedule 08.05.2015
comment
Этот способ работает, но только из расширения Postman Chrome, установив правильные заголовки для Content-Type, если я попытаюсь из браузера, у меня возникла та же проблема с шаблонами, что заставляет меня спросить: почему? в чем проблема с конфигурацией и почему неправильно настроены заголовки? Также мне нравится возвращать какой-то NotFoundException, если нет $entities, можете ли вы показать мне, как добиться этого в вашем коде? - person ReynierPM; 08.05.2015

FosRestBundle использует заголовок Accept. Это означает, что он возвращает ответ на основе того, что вы запрашиваете. Получая доступ к маршруту «app_dev.php/api/v1/reps», вы неявно запрашиваете формат html, поэтому он пытается предоставить шаблон.

Возвращает ли app_dev.php/api/v1/reps.json то, что вам нужно?

Вы также должны протестировать app_dev.php/api/v1/reps.xml и ожидать вывод xml

person pcm    schedule 08.05.2015
comment
Ни один из этих трех не работает для меня, когда я ставлю .html или .json или .xml, я получаю ошибку маршрута 404, я не могу найти то, что мне не хватает в моей конфигурации или на стороне контроллера - person ReynierPM; 08.05.2015
comment
В вашем ответе есть решение, но оно подразумевается в содержании. Вы должны конкретно указать, что для тестирования с некоторыми клиентами, такими как расширение Chrome/Firefox RestClient, нужно установить правильный заголовок: Accept:application/json - person le0diaz; 24.09.2015

Вы можете использовать просто

            $view = new View($form);
            $view->setFormat('json');
            return $this->handleView($view);
person Dr.X    schedule 25.07.2018