我正在使用FOSRestBundle构建API,我正处于需要实现创建包含二进制数据的新实体的处理阶段。
根据Sending binary data along with a REST API request
上列出的方法,multipart/form-data
发送数据对我们的实施感到最实用,因为Base64需要增加约33%的带宽。
问题
如何配置REST端点以处理请求中的文件,并在以multipart/form-data
发送数据时对JSON编码实体执行验证?
当发送原始JSON时,我一直在使用Symfony的表单handleRequest
方法对自定义FormType
执行验证。例如:
$form = $this->createForm(new CommentType(), $comment, ['method' => 'POST']);
$form->handleRequest($request);
if ($form->isValid()) {
// Is valid
}
我喜欢这种方法的原因是,我可以更好地控制实体的数量,具体取决于操作是更新(PUT)还是新操作(POST)。
我理解Symfony的Request
对象处理请求,以前JSON数据将是content
变量,但现在键入request->parameters->[form key]
以及文件包中的文件({ {1}})。
答案 0 :(得分:5)
似乎没有干净的方法来检索表单数据的Content-Type而不解析原始请求。
如果您的API仅支持json输入,或者您可以添加自定义标头(请参阅下面的注释),则可以使用此解决方案:
首先,您必须实现自己的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));
}
}
然后在你的配置文件中:
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);
使用此请求格式(form
必须替换为您的表单名称):
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--
答案 1 :(得分:1)
以下是更明确的解决方案:http://labs.qandidate.com/blog/2014/08/13/handling-angularjs-post-requests-in-symfony/
将此代码复制并粘贴到其他控制器是非常WET,我们喜欢DRY!
如果我告诉您,您可以将其应用于每个JSON请求而不必担心怎么办?我们>写了一个事件监听器 - 当标记为kernel.event_listener时 - 将:
检查请求是否是JSON请求 如果是这样,解码JSON 填充Request :: $ request对象 出现问题时返回HTTP 400错误请求。 查看https://github.com/qandidate-labs/symfony-json-request-transformer的代码! 注册此事件监听器非常简单。只需将以下内容添加到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>
答案 2 :(得分:1)
放弃并查看为图像上传设置单独端点的替代选项。例如:
POST /comments
POST /comments/{id}/image
我发现已经有一个包提供了各种RESTful上传过程。其中一个是我最初希望能够在提取文件时将multipart/form-data
解析为实体的那个。
答案 3 :(得分:0)
修改应用以在JSON中发送文件内容。
JSON
JSON
发送到服务器。您可以使用base64编码的字符串获取文件内容。然后,您可以解码并验证它。
您的JSON
将如下所示:
{
name: 'Foo',
phone: '123.345.678',
profile_image: 'R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw=='
}