用于重置密码的 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');
}
我想使用 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');
}