Api平台上传图片–如何解决未知错误?

时间:2018-12-29 04:34:45

标签: image-uploading symfony4 php-7.1 api-platform.com vichuploaderbundle

我正在使用Symfony 4,ApiPlatform 1和VichUploaderBundle 1。

按照说明in docs进行了所有操作,但出现错误:Cannot validate values of type \"NULL\" automatically. Please provide a constraint.

我什至不知道如何调试它-错误和回溯非常无用。从哪里开始搜索?请帮助。


我的图片实体:

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ImageRepository")
 * @ApiResource(iri="http://schema.org/MediaObject")
 * @Vich\Uploadable
 */
class Image
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"user"})
     */
    private $id;

    /**
     * @var File|null
     * @Assert\NotNull()
     * @Vich\UploadableField(mapping="image", fileNameProperty="contentUrl")
     */
    public $file;

    /**
     * @var string|null
     * @ORM\Column(nullable=true)
     * @ApiProperty(iri="http://schema.org/contentUrl")
     * @Groups({"user"})
     */
    private $contentUrl;

    public function getId(): ?int { return $this->id; }
    public function getContentUrl(): ?string { return $this->contentUrl; }

    public function setContentUrl(string $contentUrl): void { $this->contentUrl = $contentUrl; }
}

路由Yaml配置:

app_image_upload:
    methods : ['POST']
    path    : '/images'
    defaults:
        _controller        : App\Controller\CreateImageAction
        _api_resource_class: App\Entity\Image
        _api_receive       : false

为其自定义操作:

<?php

namespace App\Controller;

use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
use App\Entity\Image;
use App\Form\ImageType;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;

final class CreateImageAction
{
    private $validator;
    private $doctrine;
    private $factory;
    private $uploader;

    public function __construct(
        RegistryInterface $doctrine,
        FormFactoryInterface $factory,
        ValidatorInterface $validator,
        UploaderHelper $uploader
    ) {
        $this->validator = $validator;
        $this->doctrine  = $doctrine;
        $this->factory   = $factory;
        $this->uploader  = $uploader;
    }

    public function __invoke(Request $request): Image
    {
        $image = new Image();

        $form = $this->factory->create(ImageType::class, $image);
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->doctrine->getManager();
            $em->persist($image);
            $em->flush();

            $image->setContentUrl($this->uploader->asset($image, 'file'));
            $em->merge($image);
            $em->flush();

            // Prevent the serialization of the file property
            $image->file = null;

            return $image;
        }

        // This will be handled by API Platform and returns a validation error.
        throw new ValidationException($this->validator->validate($image));
    }
}

和形式:

<?php

namespace App\Form;

use App\Entity\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class ImageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('file', FileType::class, [
                'label'    => 'label.file',
                'required' => false,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class'      => Image::class,
            'csrf_protection' => false,
        ]);
    }

    public function getBlockPrefix(): string
    {
        return '';
    }
}

我的请求如下:

$ http -v --form 'api.example.com/api/images' @image.png
POST /api/images HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Authorization: Bearer [API_TOKEN]
Connection: keep-alive
Content-Length: 48888
Content-Type: multipart/form-data; boundary=aedf69840efb693f8ef3f566bc1be656
Host: api.example.com
User-Agent: HTTPie/1.0.2

+-----------------------------------------+
| NOTE: binary data not shown in terminal |
+-----------------------------------------+

然后回应:

HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache, private
Connection: keep-alive
Content-Type: application/ld+json; charset=utf-8
Date: Sat, 29 Dec 2018 04:10:27 GMT
Link: <http://api.example.com/api/docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-Frame-Options: deny

{
    "@context": "/api/contexts/Error",
    "@type": "hydra:Error",
    "hydra:description": "Cannot validate values of type \"NULL\" automatically. Please provide a constraint.",
    "hydra:title": "An error occurred",
    "trace": [
        {
            "args": [],
            "class": "",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/validator/Validator/RecursiveContextualValidator.php",
            "function": "",
            "line": 166,
            "namespace": "",
            "short_class": "",
            "type": ""
        },
        {
            "args": [
                [
                    "null",
                    null
                ],
                [
                    "null",
                    null
                ],
                [
                    "array",
                    [
                        [
                            "string",
                            "Default"
                        ]
                    ]
                ]
            ],
            "class": "Symfony\\Component\\Validator\\Validator\\RecursiveContextualValidator",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/validator/Validator/RecursiveValidator.php",
            "function": "validate",
            "line": 100,
            "namespace": "Symfony\\Component\\Validator\\Validator",
            "short_class": "RecursiveContextualValidator",
            "type": "->"
        },
        {
            "args": [
                [
                    "null",
                    null
                ],
                [
                    "null",
                    null
                ],
                [
                    "null",
                    null
                ]
            ],
            "class": "Symfony\\Component\\Validator\\Validator\\RecursiveValidator",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/validator/Validator/TraceableValidator.php",
            "function": "validate",
            "line": 66,
            "namespace": "Symfony\\Component\\Validator\\Validator",
            "short_class": "RecursiveValidator",
            "type": "->"
        },
        {
            "args": [
                [
                    "null",
                    null
                ],
                [
                    "null",
                    null
                ],
                [
                    "null",
                    null
                ]
            ],
            "class": "Symfony\\Component\\Validator\\Validator\\TraceableValidator",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/api-platform/core/src/Bridge/Symfony/Validator/Validator.php",
            "function": "validate",
            "line": 61,
            "namespace": "Symfony\\Component\\Validator\\Validator",
            "short_class": "TraceableValidator",
            "type": "->"
        },
        {
            "args": [
                [
                    "null",
                    null
                ],
                [
                    "array",
                    {
                        "groups": [
                            "null",
                            null
                        ]
                    }
                ]
            ],
            "class": "ApiPlatform\\Core\\Bridge\\Symfony\\Validator\\Validator",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/api-platform/core/src/Validator/EventListener/ValidateListener.php",
            "function": "validate",
            "line": 59,
            "namespace": "ApiPlatform\\Core\\Bridge\\Symfony\\Validator",
            "short_class": "Validator",
            "type": "->"
        },
        {
            "args": [
                [
                    "object",
                    "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
                ],
                [
                    "string",
                    "kernel.view"
                ],
                [
                    "object",
                    "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
                ]
            ],
            "class": "ApiPlatform\\Core\\Validator\\EventListener\\ValidateListener",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/event-dispatcher/Debug/WrappedListener.php",
            "function": "onKernelView",
            "line": 111,
            "namespace": "ApiPlatform\\Core\\Validator\\EventListener",
            "short_class": "ValidateListener",
            "type": "->"
        },
        {
            "args": [
                [
                    "object",
                    "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
                ],
                [
                    "string",
                    "kernel.view"
                ],
                [
                    "object",
                    "Symfony\\Component\\EventDispatcher\\EventDispatcher"
                ]
            ],
            "class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/event-dispatcher/EventDispatcher.php",
            "function": "__invoke",
            "line": 212,
            "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
            "short_class": "WrappedListener",
            "type": "->"
        },
        {
            "args": [
                [
                    "array",
                    [
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ],
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ],
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ],
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ],
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ],
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ],
                        [
                            "object",
                            "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
                        ]
                    ]
                ],
                [
                    "string",
                    "kernel.view"
                ],
                [
                    "object",
                    "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
                ]
            ],
            "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/event-dispatcher/EventDispatcher.php",
            "function": "doDispatch",
            "line": 44,
            "namespace": "Symfony\\Component\\EventDispatcher",
            "short_class": "EventDispatcher",
            "type": "->"
        },
        {
            "args": [
                [
                    "string",
                    "kernel.view"
                ],
                [
                    "object",
                    "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
                ]
            ],
            "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php",
            "function": "dispatch",
            "line": 142,
            "namespace": "Symfony\\Component\\EventDispatcher",
            "short_class": "EventDispatcher",
            "type": "->"
        },
        {
            "args": [
                [
                    "string",
                    "kernel.view"
                ],
                [
                    "object",
                    "Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent"
                ]
            ],
            "class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/http-kernel/HttpKernel.php",
            "function": "dispatch",
            "line": 155,
            "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
            "short_class": "TraceableEventDispatcher",
            "type": "->"
        },
        {
            "args": [
                [
                    "object",
                    "Symfony\\Component\\HttpFoundation\\Request"
                ],
                [
                    "integer",
                    1
                ]
            ],
            "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/http-kernel/HttpKernel.php",
            "function": "handleRaw",
            "line": 67,
            "namespace": "Symfony\\Component\\HttpKernel",
            "short_class": "HttpKernel",
            "type": "->"
        },
        {
            "args": [
                [
                    "object",
                    "Symfony\\Component\\HttpFoundation\\Request"
                ],
                [
                    "integer",
                    1
                ],
                [
                    "boolean",
                    true
                ]
            ],
            "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/vendor/symfony/http-kernel/Kernel.php",
            "function": "handle",
            "line": 198,
            "namespace": "Symfony\\Component\\HttpKernel",
            "short_class": "HttpKernel",
            "type": "->"
        },
        {
            "args": [
                [
                    "object",
                    "Symfony\\Component\\HttpFoundation\\Request"
                ]
            ],
            "class": "Symfony\\Component\\HttpKernel\\Kernel",
            "file": "/Users/SOMEUSER/Projects/MYPROJECT/www/public/index.php",
            "function": "handle",
            "line": 37,
            "namespace": "Symfony\\Component\\HttpKernel",
            "short_class": "Kernel",
            "type": "->"
        }
    ]
}

UPD:MWE on GitHub

1 个答案:

答案 0 :(得分:1)

通过更精确地遵循文档,一切正常:您只是忘记了正确的new Subject<boolean>().next(true)批注和@ApiResource中的use语句

Image.php