Serialize POST request body into array with FOSRestBundle
Asked Answered
C

2

6

I am trying to make a Rest web service using Symfony 3 and FOSRestBundle.
I come from a Spring + Jackson background so i'm trying to make it so that you can pass objects to controllers as request body that become function parameters and return objects that get serialized into json, so far i managed to make it work for everything except for arrays.
This is my code:

configuration:

#FOSRestBundle
fos_rest:
    param_fetcher_listener: true
    body_listener: 
        enabled: true
        decoders:
            json: fos_rest.decoder.json
    format_listener:
        rules:
            - { path: ^/, priorities: [ json ], fallback_format: json, prefer_extension: true }

    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

This is the controller

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use JMS\DiExtraBundle\Annotation\Inject;
use FOS\RestBundle\Controller\Annotations\Post;
use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Controller\FOSRestController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;

class DefaultController extends FOSRestController {

    /**
     * @Post("/rest", name="rest_test")
     * @ParamConverter("myArr", converter="fos_rest.request_body")
     * @View
     * @param Request $request
     */
    public function restAction(Request $request, array $myArr) {
        return $myArr;
    }
}

When i try to call this from my rest client (putting [1, 2] as request body) i receive this error:

{
  "code": 500,
  "message": "Converter 'fos_rest.request_body' does not support conversion of parameter 'myArr'."
}

If i turn myArr into an object (that is i change its type from array to MyObject, containing the number variable myVar) and send data that reflects that object structure (such as {"myVar": 2} ) it works fine, but it doesn't work with an array.

Cadmarr answered 29/9, 2016 at 19:16 Comment(0)
C
11

After days of toying with the code and this nice conversation with one of the collaborators of FoS https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1582 i have discovered that, if you are using the JMS Serializer Bundle, you can just write array in the class field of the param converter and it will deserialize it into an array. I should've tried it earlier i guess

@ParamConverter("myarr", class="array", converter="fos_rest.request_body")

It can even deserialize arrays of objects (as long as they are the same class, i guess)

@ParamConverter("myarr", class="array<Namespace/Class>", converter="fos_rest.request_body")

UPDATE I'm updating this answer since i stopped using JMS Serializer and started using the Symfony Serializer, which has a different syntax for arrays of objects:

@ParamConverter("myarr", class="Namespace/Class[]", converter="fos_rest.request_body")

Normal arrays syntax is untouched

Cadmarr answered 8/10, 2016 at 13:5 Comment(2)
Hi, could you help me please, i am trying to configure fos_rest to use symfony serializer, but cound not find any solution, how did you configure it ?Galley
@AndrewVakhniuk the configuration i'm using is all posted in my question, but this is for Symfony 3 and i haven't been working on it for a while so it might be different now. Have you checked their github?Cadmarr
G
1

The FOS Rest body convertor purpose is to populate objects, not arrays. You can try to implement your own param converter (see this documentation), but I'm really not sure you can achieve what you want.

Anyway, dealing with objects wouldn't be cleaner? It would allow you to be sure that the data you're receiving match what you expect, to use validations, and so on...

Graniela answered 30/9, 2016 at 8:39 Comment(4)
I can always create an object with an array inside i guess (haven't tried yet). But there are situations in which i have to send, for example, an array of ids to tell the backend to do operations on a set of itemsCadmarr
Maybe it's your REST design that is not correct then? REST is not really designed to do operations on multiple ressources with one request. If you really need to do so, I think you will have to do it manually, because FOS REST is not designed for that. :/Graniela
The fosrestbundle documentation even says that you can use the PATCH auto generated method (haven't tried yet) to partially update a subset of resources symfony.com/doc/current/bundles/FOSRestBundle/… , still it was just an example...i'm pretty confident there's a lot of use cases where you might need to sen an array in a REST call. Also, Spring + Jackson lets you send an array (of any kind) as request bodyCadmarr
Indeed there is, I'm just saying that it's not the most generic use case and that it needs to be handled manually. This is becoming a matter of opinion, I don't think there really is a standard for that, but IMO, resources identifiers should be kept in the URI. Or as a filter in a query param, if necessary, but the body should be kept for the resources themselves. If you do it with a query param, The FOS Rest QueryParam annotation would allow you to retrieve an array of ID, but there's still at least one code line to manage that.Graniela

© 2022 - 2024 — McMap. All rights reserved.