Rest 多部分/表单数据和表单类型验证

Rest 多部分/表单数据和表单类型验证,rest,symfony,fosrestbundle,Rest,Symfony,Fosrestbundle,我正在使用和构建一个API,在这个阶段我需要实现对包含二进制数据的新实体的创建的处理 由于Base64需要增加约33%的带宽,因此遵循以multipart/form data的形式发送数据的方法对我们的实现来说最为实用 问题 当以多部分/form data的形式发送数据时,如何配置REST端点来处理请求中的文件并对JSON编码的实体执行验证 在发送原始JSON时,我一直在使用Symfony的formhandleRequest方法对自定义的FormType执行验证。例如: $form = $thi

我正在使用和构建一个API,在这个阶段我需要实现对包含二进制数据的新实体的创建的处理

由于Base64需要增加约33%的带宽,因此遵循以
multipart/form data
的形式发送数据的方法对我们的实现来说最为实用

问题

当以
多部分/form data
的形式发送数据时,如何配置REST端点来处理请求中的文件并对JSON编码的实体执行验证

在发送原始JSON时,我一直在使用Symfony的form
handleRequest
方法对自定义的
FormType
执行验证。例如:

$form = $this->createForm(new CommentType(), $comment, ['method' => 'POST']);
$form->handleRequest($request);

if ($form->isValid()) {

  // Is valid

}
我喜欢这种方法的原因是,我可以根据操作是更新(PUT)还是新(POST)对实体的填充进行更多的控制


我知道Symfony的
请求
对象处理请求,因此以前JSON数据将是
内容
变量,但现在被键入
请求->参数->[form key]
和文件包中的文件(
请求->文件
).

似乎没有干净的方法可以在不解析原始请求的情况下检索表单数据的内容类型

如果您的API仅支持json输入,或者您可以添加自定义头(请参阅下面的注释),则可以使用此解决方案:

首先,您必须实现自己的
body\u侦听器

然后在配置文件中:

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
最后,您只需在控制器中调用
handleRequest
。例:

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

$form->handleRequest($request);
使用此请求格式(
表单
必须替换为您的表单名称):


以下是更明确的解决方案:

复制和粘贴此代码到其他控制器是非常湿,我们喜欢干

如果我告诉您可以将此应用于每个JSON请求,而不必担心它会怎么样?我们>编写了一个事件侦听器,当标记为kernel.event_侦听器时,它将:

检查请求是否为JSON请求 如果是,请解码JSON 填充请求::$Request对象 出错时返回HTTP 400错误请求。 检查代码在! 注册此事件侦听器非常简单。只需将以下内容添加到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>
class=“Qandidate\Common\Symfony\HttpKernel\EventListener\JsonRequestTransformerListener”>

修改应用程序以发送JSON格式的文件内容

  • 读取应用程序中的文件内容
  • Base64对文件内容进行编码
  • 创建一个包含所有字段的
    JSON
    (包括包含文件内容的字段)
  • JSON
    发送到服务器
  • 以标准的方式处理它
  • 您以base64编码字符串获取文件内容。然后,您可以对其进行解码和验证

    您的
    JSON
    将如下所示:

    {
        name: 'Foo',
        phone: '123.345.678',
        profile_image: 'R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw=='
    }
    

    在放弃并考虑另一种选择,即为图像上载使用单独的端点之后。例如:

    $form = $this->createForm(new CommentType(), $comment, ['method' => 'POST']);
    $form->handleRequest($request);
    
    if ($form->isValid()) {
    
      // Is valid
    
    }
    
  • 创建新注释
  • POST/comments

  • 将图像上载到端点
  • POST/comments/{id}/image

    我发现已经有一个包提供了各种RESTful上传过程。其中之一就是我最初希望能够在提取文件的同时将
    多部分/表单数据
    解析为实体



    发送到web服务的请求是来自浏览器还是来自您可以修改的应用程序/客户端?我们可以修改的应用程序和客户端有多少用户使用IE<10?它们可以忽略不计吗?所有客户端都将是移动应用程序。主要是本机应用程序。我已经更新了答案,将解码部分移动到FOS body listener。不幸的是,在我的情况下,这将不起作用,因为数据将作为
    多部分/表单数据发送,并且
    $content
    的值为null@Malachi您可以修改此条件以检查api uri(例如/api/),例如,OP使用的不是FOSRestBundle,它提供了这样一种功能,我宁愿避免使用base64,因为所需的数据开销约为33%。您可以使用不同的编码。使用此方法,此讨论可能很有用(请参阅所有注释),表单组件不会直接处理该文件。看起来他们已经实现了自己的解析器,但没有处理绑定多个文件的atm@see当添加此代码时,它会产生一个通知:
    未定义的属性:VSmart\ApiBundle\Listener\BodyListener::$decoderProvider
    。与提供的代码不同的是,我们不使用表单来处理请求。尽管调试器显示$decoderProvider确实存在。有什么想法吗?@roelvelhuizen您可以跳过类的
    扩展部分,使用完整的服务定义,而不是使用
    fos\u rest.body\u listener
    作为
    父项。我将更新我的应答感谢,这在使用phpunit测试时似乎有效,但在通过高级rest客户端发送相同的请求时,正文仍然是空的。有什么想法吗?你使用哪一个客户?我创建了一个单独的问题,这个案例可能与原始答案有点不同。
    
    {
        name: 'Foo',
        phone: '123.345.678',
        profile_image: 'R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw=='
    }