You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

API Platform自定义重置密码接口报Invalid identifier错误如何解决

问题背景

我需要使用API Platform实现用户密码重置,最初创建的自定义端点配置如下:

itemOperations: [
    'sendEmail' => [
        'method' => 'patch',
        'path' => '/users/password/send/token',
        'controller' => ResetPasswordRequestsController::class,
        'normalization_context' => ['groups' => [
            'read:sendEmail:user:item', 
        ]],
    ],
]

控制器逻辑为通过邮箱查找用户并创建密码重置token。发送请求时收到"Invalid identifier value or configuration."错误,请求信息如下:

请求地址: http://192.168.0.11:8000/api/users/password/send/token
请求体:
{
    "email": "user@email.com"
}

初步判断问题为:API Platform的PATCH方法要求传入User实体的id,但本次请求仅传入了邮箱参数。


User实体属性定义

class User implements UserInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */    
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)
     * @Assert\NotBlank(groups={"sendEmail"})
     */
    private $email;

    /**
     * @ORM\Column(type="json")
     */
    private $roles = [];

    /**
     * @var string The hashed password
     * @ORM\Column(type="string")
     * @Assert\NotBlank
     */
    private $password;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank
     */
    private $firstName;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank
     */
    private $lastName;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

    /**
     * @ORM\OneToOne(targetEntity=Teacher::class, mappedBy="user", cascade={"persist", "remove"})
     */
    private $teacher;

    /**
     * @ORM\OneToOne(targetEntity=Student::class, mappedBy="user", cascade={"persist", "remove"})
     */
    private $student;

    /**
     * @ORM\ManyToMany(targetEntity=Role::class, mappedBy="users")
     */
    private $dbroles;

    /**
     * @ORM\OneToMany(targetEntity=Invitation::class, mappedBy="owner")
     */
    private $invitations;

    /**
     * @ORM\OneToMany(targetEntity=Inscription::class, mappedBy="userRegister",cascade={"persist"})
     */
    private $inscriptions;

    /**
     * @ORM\OneToMany(targetEntity=UserInvitation::class, mappedBy="owner")
     */
    private $userInvitations;

    /**
     * @ORM\OneToMany(targetEntity=UserInvitation::class, mappedBy="guest")
     */
    private $userInvitationsGuest;

}

控制器代码

<?php

namespace App\Controller;

use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class ResetPasswordRequestsController extends AbstractController
{
    use ResetPasswordControllerTrait;

    private $mailer;
    private $resetPasswordHelper;
    private $passwordEncoder;

    public function __construct(ValidatorInterface $validator, MailerInterface $mailer, ResetPasswordHelperInterface $resetPasswordHelper, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->mailer = $mailer;
        $this->resetPasswordHelper = $resetPasswordHelper;
        $this->passwordEncoder = $passwordEncoder;
        $this->validator = $validator;
    }

    public function  __invoke(Request $request)
    {        
        $data = $request->get('data');

        if($data->getEmail()){
            $this->sendEmail($data->getEmail());
        }
        if($data->getPassword() && $request->get("token")){
            $this->resetPassword($data->getPassword(), $request->get("token"));
        }
        
    }

    public function resetPassword($newPassword, $token){

        try {
            $user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
        } catch (ResetPasswordExceptionInterface $e) {
            $this->addFlash('reset_password_error', sprintf(
                'There was a problem validating your reset request - %s',
                $e->getReason()
            ));

            return $this->redirectToRoute('app_forgot_password_request');
        }

        $this->resetPasswordHelper->removeResetRequest($token);

        $encodedPassword = $this->passwordEncoder->encodePassword(
            $user,
            $newPassword
        );

        $user->setPassword($encodedPassword);
        $this->getDoctrine()->getManager()->flush();

            // The session is cleaned up after the password has been changed.
        $this->cleanSessionAfterReset();

        return $user;
    }

    public function sendEmail($email){
        $user = $this->getDoctrine()->getRepository(User::class)->findOneBy([
            'email' => $email,
        ]);

        if (!$user) {
            throw new Exception(sprintf("L'utilisateur est introuvable"));
        }

        try {
            $resetToken = $this->resetPasswordHelper->generateResetToken($user);
        } catch (ResetPasswordExceptionInterface $e) {
            throw new Exception(sprintf("Erreur lors de la génération du token"));
        }
        
        $token = $resetToken->getToken();
        $url = $this->getParameter('front_app_url');
        $redirectUrl =  $url . "/auth/reset/" . $token;

        $email = (new TemplatedEmail())
            ->from(new Address('mouchelet.thomas@gmail.com', 'thomas mouchelet'))
            ->to($user->getEmail())
            ->subject('Your password reset request')
            ->htmlTemplate('reset_password/email.html.twig')
            ->context([
                'resetToken' => $resetToken,
                'redirectUrl' => $redirectUrl
            ])
        ;

        $this->mailer->send($email);
        
        $this->setTokenObjectInSession($resetToken);

        return $user; 
    }
}

问题更新:调整配置后新报错

按照建议调整为集合操作配置如下:

collectionOperations: [
    'sendEmail' => [
        'write' => false,
        'serialize' => false,
        'method' => 'post',
        'path' => '/users/password/send/token',
        'controller' => ResetPasswordRequestsController::class,
    ],
],

同时尝试添加验证组配置:

collectionOperations: [
  'post' => ['validation_groups' => ['Default', 'post:user']],
]

实体注解同步调整:

@Assert\NotBlank(groups={"post:user"})

调整后出现新报错:

Cannot validate values of type "null" automatically. Please provide a constraint.

内容的提问来源于stack exchange,提问作者ThomasM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 22:39:02