包含对象的对象的 Swagger 注释问题

Swagger annotation problems with objects containing objects

我正在 Symfony 4 项目中休息 api 编写端到端测试。

我用的是php7.4,Swagger注解,nelmio/api-doc-bundle3.6.1,nelmio/cors-bundle1.5.6

这是我的方法控制器代码。

/**
 * @Route(path="", name="create", methods={"POST"})
 * @ValidationGroups({"create"})
 *
 * @SWG\Post(
 *     path="/api/professions",
 *     tags={"Endpoint: Professions"},
 *     summary="save a profession",
 *     @SWG\Parameter(
 *         name="ProfessionDto",
 *         required=true,
 *         in="body",
 *         @Model(type=ProfessionDto::class)
 *     ),
 *     @SWG\Response(
 *         response=201,
 *         description="created",
 *     )
 * )
 */
public function add(ProfessionDto $professionDto): CreatedView
{
    $professionDto = $this->professionService->insert($professionDto);

    return $this->created(['profession' => $professionDto]);
}

ProfessionDto 是定义交换数据的对象。它包含 属性 一些更多的对象,因为我想在返回的数据中有一些结构,而不仅仅是一堆键值对。

在 class ProfessionDto 中,我定义了与其他对象相关的属性,例如:


namespace App\Api\Dto;

use Nelmio\ApiDocBundle\Annotation\Model;
use Swagger\Annotations as SWG;
use Symfony\Component\Serializer\Annotation\Groups as SerializerGroups;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @SWG\Definition()
 */
class ProfessionDto
{
    use HistoryDto;

    /**
     * @SWG\Property(description="Id", type="integer")
     *
     * @SerializerGroups({"view", "update", "collection"})
     */
    public ?int $id = null;

    /**
     * @Assert\NotBlank(groups={"create"})
     * @Assert\Type("string")
     * @SWG\Property(description="...", type="string")
     */
    public string $name;

    /**
     * @Assert\Type("boolean")
     * @SWG\Property(description="ist aktiviert/deaktiviert", type="boolean")
     */
    public bool $active;

    /**
     * @SWG\Property(
     *     description="...",
     *     type="object",
     *     ref=@Model(type=ProfessionAreaDto::class)
     * )
     *
     * @SerializerGroups({"view", "create", "update"})
     */
    public ?ProfessionAreaDto $professionArea = null;

    /**
     * @SWG\Property(
     *     description="...",
     *     type="object",
     *     ref=@Model(type=ProfessionalActivityDto::class)
     * )
     *
     * @SerializerGroups({"view", "create", "update"})
     */
    public ?ProfessionalActivityDto $professionalActivity = null;

    /**
     * @SWG\Property(
     *     description="...",
     *     type="object",
     *     ref=@Model(type=IntroductionDto::class)
     * )
     *
     * @SerializerGroups({"view", "create", "update"})
     */
    public ?IntroductionDto $introduction = null;

    /**
     * @SWG\Property(
     *     description="...",
     *     type="object",
     *     ref=@Model(type=PerformanceBehaviourDto::class)
     * )
     *
     * @SerializerGroups({"view", "create", "update"})
     */
    public ?PerformanceBehaviourDto $performanceBehaviour = null;
}

如果我用邮递员或通过我的测试调用 api 并将我的数据作为 json 传递,例如

{
  "id":null,
  "name":"fancy new data",
  "active":true,
  "professionArea":{
    "id":48,
    "name":"Vitae nulla aperiam aut enim.",
    "active":true,
    "signature":null,
    "signatureTypeId":null,
    "transition":null,
    "infotextCheck":null
  },
  "professionalActivity":{
    "id":null,
    "textMResignationTestimonial":null,
    ...
    "changedBy":null
  },
  "introduction":{
    "id":null,
    "textMResignationTestimonial":null,
    "textMInterimTestimonial":null,
    ...
    "changedOn":null,
    "changedBy":null
  },
  "performanceBehaviour":{
    "id":null,
    "textMResignationGrade1":null,
    "textMInterimGrade1":null,
    ...
    "changedOn":null,
    "changedBy":null
  },
  "createdOn":null,
  "createdBy":null,
  "changedOn":null,
  "changedBy":null
}

我收到错误消息: TypeError: Typed 属性 App\Api\Dto\ProfessionDto::$professionArea 必须是 App\Api\Dto\ProfessionAreaDto 的实例或 null,使用数组

我做错了什么?我期望太多了吗?对象中的对象不可能吗?

因为这个项目没有使用 friendsofsymfony/rest-bundle 并且 Controller 没有扩展 AbstractFOSRestController。

所以我的问题的解决方案是手动编写一个 Denormalizer class,它将我的数组映射回正确的对象。
我是这样做的:


namespace App\Serializer;


use App\Api\Dto\IntroductionDto;
use App\Api\Dto\PerformanceBehaviourDto;
use App\Api\Dto\ProfessionalActivityDto;
use App\Api\Dto\ProfessionAreaDto;
use App\Api\Dto\ProfessionDto;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

class ProfessionDeNormalizer implements DenormalizerInterface
{
    public function denormalize($data, $type, $format = null, array $context = [])
    {
        $dto = new ProfessionDto();
        $areaDto = new ProfessionAreaDto();
        $introDto = new IntroductionDto();
        $activityDto = new ProfessionalActivityDto();
        $performanceDto = new PerformanceBehaviourDto();

        foreach($data['professionArea'] as $key => $value) {
            $areaDto->$key = $value;
        }
        unset($data['professionArea']);

        foreach($data['introduction'] as $key => $value) {
            $introDto->$key = $value;
        }
        unset($data['introduction']);

        foreach($data['professionalActivity'] as $key => $value) {
            $areaDto->$key = $value;
        }
        unset($data['professionalActivity']);

        foreach($data['performanceBehaviour'] as $key => $value) {
            $areaDto->$key = $value;
        }
        unset($data['performanceBehaviour']);

        foreach($data as $key => $value) {
            $dto->$key = $value;
        }

        $dto->professionArea = $areaDto;
        $dto->professionalActivity = $activityDto;
        $dto->performanceBehaviour = $performanceDto;
        $dto->introduction = $introDto;

        return $dto;
    }

    public function supportsDenormalization($data, $type, $format = null)
    {
        return ProfessionDto::class === $type;
    }

}