用于重置密码的 ApiPlatform DataProvider

问题描述

我想使用 DataProvider 来通过令牌重置密码。 我对 User 实体

进行了自定义操作
"reset_password"={
 *                      "method"="PATCH",*                      "path"="/user/{token}/password/reset",*                      "requirements"={"token"="^\w{32}$"},*                      "controller"=ResetPassword::class,*                      "normalization_context"={"groups"={"user:read"}},*                      "denormalization_context"={"groups"={"user-res-p:write"}},*                      "validation_groups"={"ResetPassword"},*                      "openapi_context"={
 *                          "summary"="Reset della password dell'utente.",*                          "description"="Permette all'utente di modificare la sua password in caso l'abbia smarrita.<br>
                                           Questo endpoint è aperto al pubblico e non necessita di autenticazione."
 *                      }
 *                  },

在请求的正文中客户端必须传递密码和confirmPassword

{
    "plainPassword": "mynewpassword","confirmPassword": "mynewpassword"
}

这是我的控制器

namespace App\Controller\Security;


use App\Entity\Security\User;
use App\Repository\Security\UserRepository;
use Exception;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class ResetPassword
{
    /**
     * @param string $token
     * @param Request $request
     * @param UserRepository $repository
     * @param UserPasswordEncoderInterface $passwordEncoder
     *
     * @return User
     * @throws Exception
     */
    public function __invoke(User $data,Request $request,UserRepository $repository,UserPasswordEncoderInterface $passwordEncoder): User
    {
        $content = json_decode($request->getContent());

        /** @var User $user */
        //$user = $repository->getUserByResetPasswordToken($token);
        $user = $data;

        if(null === $user){
            throw new NotFoundHttpException("Utente non trovato.");
        }

        $user->setPlainPassword($content->plainPassword);
        $user->setConfirmPassword($content->confirmPassword);

        $user->setPassword($passwordEncoder->encodePassword($user,$content->plainPassword));

        $user->setRenewPasswordToken(null);
        $user->setRenewPasswordTokenExpiration(null);
        return $user;
    }
}

但是我遇到了这个错误

{
    "@context": "/api/contexts/Error","@type": "hydra:Error","hydra:title": "An error occurred","hydra:description": "Invalid identifier value or configuration.","trace": [
        {
            "namespace": "","short_class": "","class": "","type": "","function": "","file": "/path/to/project/vendor/api-platform/core/src/EventListener/ReadListener.PHP","line": 112,"args": []
        },{
            "namespace": "ApiPlatform\\Core\\EventListener","short_class": "ReadListener","class": "ApiPlatform\\Core\\EventListener\\ReadListener","type": "->","function": "onKernelRequest","file": "/path/to/project/vendor/symfony/event-dispatcher/Debug/WrappedListener.PHP","line": 117,{
            "namespace": "Symfony\\Component\\Eventdispatcher\\Debug","short_class": "WrappedListener","class": "Symfony\\Component\\Eventdispatcher\\Debug\\WrappedListener","function": "__invoke","file": "/path/to/project/vendor/symfony/event-dispatcher/Eventdispatcher.PHP","line": 230,{
            "namespace": "Symfony\\Component\\Eventdispatcher","short_class": "Eventdispatcher","class": "Symfony\\Component\\Eventdispatcher\\Eventdispatcher","function": "callListeners","line": 59,"function": "dispatch","file": "/path/to/project/vendor/symfony/event-dispatcher/Debug/TraceableEventdispatcher.PHP","line": 151,"short_class": "TraceableEventdispatcher","class": "Symfony\\Component\\Eventdispatcher\\Debug\\TraceableEventdispatcher","file": "/path/to/project/vendor/symfony/http-kernel/HttpKernel.PHP","line": 133,{
            "namespace": "Symfony\\Component\\HttpKernel","short_class": "HttpKernel","class": "Symfony\\Component\\HttpKernel\\HttpKernel","function": "handleRaw","line": 79,"function": "handle","file": "/path/to/project/vendor/symfony/http-kernel/Kernel.PHP","line": 195,"short_class": "Kernel","class": "Symfony\\Component\\HttpKernel\\Kernel","file": "/path/to/project/public/index.PHP","line": 28,"args": []
        }
    ]
}

数据提供者:

namespace App\DataProvider\Security;


use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use App\Entity\Security\User;
use App\Repository\Security\UserRepository;


class UserResetPasswordDataProvider implements ItemDataProviderInterface,RestrictedDataProviderInterface
{
    private UserRepository $repository;

    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    public function getItem(string $resourceClass,$id,string $operationName = null,array $context = [])
    {   dump($id);
        return $this->repository->getUserByResetPasswordToken($id);

    }

    public function supports(string $resourceClass,array $context = []): bool
    {
        dump($resourceClass);
        return User::class === $resourceClass && $operationName === 'reset_password';
    }
}

解决方法

实际上,使用 API Platform 执行此操作的最佳方法是使用类似 /users/{id}/reset-password?hash= 的路由,以便 API P 可以为用户提供,然后您的工作只是检查访问权限。从 RESTFUL 的角度来看,它也更有意义。 (资源应在 URI 中标识)

您在此处遇到的错误是因为 API P 认为您在查询中拥有的哈希值是一个 id。探查器(可从 http://localhost/_profiler/ 获得)可能会告诉您更多关于此的信息,因为您之前有一个例外(滚动到例外页面的底部)。 >

不管怎样,问题很可能出在dataprovider上。您可以通过定义自己的方式来修复它。这个is documented here。这里有一个棘手的部分:您的数据提供者的设备的条件基于查询中发生的情况。因此,您的支持方法可能如下所示:

public function supports(string $resourceClass,string $operationName = null,array $context = []): bool
{
    return User::class === $resourceClass && $this->requestStack->getMasterRequest()->has('hash');
}