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
相关产品推荐
相关产品推荐

