用于重置密码的 ApiPlatform DataProvider

ApiPlatform DataProvider for reset password

我想使用 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."
 *                      }
 *                  },

在请求体中客户端必须传递密码和确认密码

{
    "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,
            "args": []
        },
        {
            "namespace": "Symfony\Component\EventDispatcher\Debug",
            "short_class": "WrappedListener",
            "class": "Symfony\Component\EventDispatcher\Debug\WrappedListener",
            "type": "->",
            "function": "__invoke",
            "file": "/path/to/project/vendor/symfony/event-dispatcher/EventDispatcher.php",
            "line": 230,
            "args": []
        },
        {
            "namespace": "Symfony\Component\EventDispatcher",
            "short_class": "EventDispatcher",
            "class": "Symfony\Component\EventDispatcher\EventDispatcher",
            "type": "->",
            "function": "callListeners",
            "file": "/path/to/project/vendor/symfony/event-dispatcher/EventDispatcher.php",
            "line": 59,
            "args": []
        },
        {
            "namespace": "Symfony\Component\EventDispatcher",
            "short_class": "EventDispatcher",
            "class": "Symfony\Component\EventDispatcher\EventDispatcher",
            "type": "->",
            "function": "dispatch",
            "file": "/path/to/project/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php",
            "line": 151,
            "args": []
        },
        {
            "namespace": "Symfony\Component\EventDispatcher\Debug",
            "short_class": "TraceableEventDispatcher",
            "class": "Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher",
            "type": "->",
            "function": "dispatch",
            "file": "/path/to/project/vendor/symfony/http-kernel/HttpKernel.php",
            "line": 133,
            "args": []
        },
        {
            "namespace": "Symfony\Component\HttpKernel",
            "short_class": "HttpKernel",
            "class": "Symfony\Component\HttpKernel\HttpKernel",
            "type": "->",
            "function": "handleRaw",
            "file": "/path/to/project/vendor/symfony/http-kernel/HttpKernel.php",
            "line": 79,
            "args": []
        },
        {
            "namespace": "Symfony\Component\HttpKernel",
            "short_class": "HttpKernel",
            "class": "Symfony\Component\HttpKernel\HttpKernel",
            "type": "->",
            "function": "handle",
            "file": "/path/to/project/vendor/symfony/http-kernel/Kernel.php",
            "line": 195,
            "args": []
        },
        {
            "namespace": "Symfony\Component\HttpKernel",
            "short_class": "Kernel",
            "class": "Symfony\Component\HttpKernel\Kernel",
            "type": "->",
            "function": "handle",
            "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, string $operationName = null, array $context = []): bool
    {
        dump($resourceClass);
        return User::class === $resourceClass && $operationName === 'reset_password';
    }
}

事实上,使用 API 平台最好的方法是使用像 /users/{id}/reset-password?hash= 这样的路由,这样 API P 就可以为用户提供服务,然后你的工作就是仅用于检查访问权限。从 RESTFUL 的角度来看,它也更有意义。 (资源应在 URI 中标识)

你这里的错误是因为 API P 认为你查询中的散列是一个 id。探查器(可在 http:///localhost/_profiler/ 获得)可能会告诉您更多相关信息,因为您之前有一个例外 (滚动到例外页面的底部).

无论如何,问题可能出在数据提供者上。你可以通过定义你自己的来修复它。这个is documented here。这里有一个棘手的部分:你的数据提供者的应用条件是基于查询中发生的事情。因此,您的支持方法可能如下所示:

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