multipart/form-data and FormType validation

徘徊边缘 提交于 2019-12-03 03:03:13

It seems that there is no clean way to retrieve the Content-Type of the form-data without parsing the raw request.

If your API does support only json input or if you can add a custom header (see comments below), you can use this solution :

First you must implements your own body_listener:

namespace Acme\ApiBundle\FOS\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use FOS\RestBundle\Decoder\DecoderProviderInterface;

class BodyListener
{
    /**
     * @var DecoderProviderInterface
     */
    private $decoderProvider;

    /**
     * @param DecoderProviderInterface $decoderProvider Provider for fetching decoders
     */
    public function __construct(DecoderProviderInterface $decoderProvider)
    {
        $this->decoderProvider = $decoderProvider;
    }

    /**
     * {@inheritdoc}
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        if (strpos($request->headers->get('Content-Type'), 'multipart/form-data') !== 0) {
            return;
        }

        $format = 'json';
        /*
         * or, using a custom header :
         *
         * if (!$request->headers->has('X-Form-Content-Type')) {
         *     return;               
         * }
         * $format = $request->getFormat($request->headers->get('X-Form-Content-Type'));
         */

        if (!$this->decoderProvider->supports($format)) {
            return;
        }

        $decoder = $this->decoderProvider->getDecoder($format);
        $iterator = $request->request->getIterator();
        $request->request->set($iterator->key(), $decoder->decode($iterator->current(), $format));
    }
}

Then in your config file :

services:
    acme.api.fos.event_listener.body:
        class: Acme\ApiBundle\FOS\EventListener\BodyListener

        arguments:
            - "@fos_rest.decoder_provider"

        tags:
            -
                name: kernel.event_listener
                event: kernel.request
                method: onKernelRequest
                priority: 10

Finally, you'll just have to call handleRequest in your controller. Ex:

$form = $this->createFormBuilder()
    ->add('foo', 'text')
    ->add('file', 'file')
    ->getForm()
;

$form->handleRequest($request);

Using this request format (form must be replace by your form name):

POST http://xxx.xx HTTP/1.1
Content-Type: multipart/form-data; boundary="01ead4a5-7a67-4703-ad02-589886e00923"
Host: xxx.xx
Content-Length: XXX


--01ead4a5-7a67-4703-ad02-589886e00923
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name=form


{"foo":"bar"}
--01ead4a5-7a67-4703-ad02-589886e00923
Content-Type: text/plain
Content-Disposition: form-data; name=form[file]; filename=foo.txt


XXXX
--01ead4a5-7a67-4703-ad02-589886e00923--

Here is more clear solution: http://labs.qandidate.com/blog/2014/08/13/handling-angularjs-post-requests-in-symfony/

Copy and pasting this code to other controllers is very WET and we like DRY!

What if I told you you could apply this to every JSON request without having to worry about it? We > wrote an event listener which - when tagged as a kernel.event_listener - will:

check if a request is a JSON request if so, decode the JSON populate the Request::$request object return a HTTP 400 Bad Request when something went wrong. Check out the code at https://github.com/qandidate-labs/symfony-json-request-transformer! Registering this event listener is really easy. Just add the following to your services.xml:

<service id="kernel.event_listener.json_request_transformer" > class="Qandidate\Common\Symfony\HttpKernel\EventListener\JsonRequestTransformerListener">
   <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="100" />
</service>

After giving up and looking at an alternative option of having a separate endpoint for the image upload. For example:

  1. Create the new comment.

POST /comments

  1. Upload image to end point

POST /comments/{id}/image

I found there is already a bundle which provides various RESTful uploading processes. One of which was the one I originally wanted of being able to parse multipart/form-data into an entity whilst extracting the file.

Modify the app to send the file content in the JSON.

  1. Read the file content in you app.
  2. Base64 encode the content of the file
  3. Create a JSON with all your field (included the one with the file content)
  4. Send the JSON to the server.
  5. Handle it in the standard way.

You get the file content in a base64 encoded string. You can then decode it and validate it.

Your JSON will look like:

{
    name: 'Foo',
    phone: '123.345.678',
    profile_image: 'R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw=='
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!