Description
Hello,
it seems handling of data returned from __invoke()
method of class that implements MessageHandlerInterface
is not correct.
Sending request to /users/password-reset-confirmation
.
Expected behavior:
Api platform should return a response object that was returned from __invoke()
.
In this particular case I'm trying to return JWTAuthenticationSuccessResponse
which extends JsonResponse
.
Current behavior:
Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\ControllerDoesNotReturnResponseException: "The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned an object of type App\Dto\User\UserPasswordResetConfirmation." at /srv/vendor/api-platform/core/src/Action/PlaceholderAction.php line 32
DTO class UserPasswordResetConfirmation.php
:
namespace App\Dto\User;
use App\Validator as AppAssert;
use Symfony\Component\Validator\Constraints as Assert;
final class UserPasswordResetConfirmation
{
/**
* @var string
* @Assert\NotBlank()
* @AppAssert\IsHash()
*/
public $passwordResetToken;
/**
* @var string
* @Assert\NotBlank()
* @AppAssert\PlainPassword()
*/
public $plainPassword;
}
API Resource config user_password_reset_confirmation.yaml
:
App\Dto\User\UserPasswordResetConfirmation:
attributes:
messenger: true
output: false
itemOperations: {}
collectionOperations:
post:
access_control: 'is_anonymous()'
path: '/users/password-reset-confirmation'
status: 200
swagger_context:
tags: ['User']
summary: 'Confirms and sets new password'
responses:
200:
description: 'Successfully set new password, returning a new JWT token'
schema:
type: 'object'
properties:
token:
type: 'string'
description: 'JWT authentication token'
400:
description: 'Invalid input'
404:
description: 'Resource not found'
Message handler class UserPasswordResetConfirmationHandler.php
:
namespace App\Handler;
use App\Dto\User\UserPasswordResetConfirmation;
use App\Event\UserEvent;
use App\Service\User\UserService;
use App\Traits\EventDispatcherable;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
class UserPasswordResetConfirmationHandler implements MessageHandlerInterface
{
use EventDispatcherable;
/**
* @var UserService
*/
private $userService;
/**
* @var AuthenticationSuccessHandler
*/
private $authenticationHandler;
public function __construct(UserService $userService, AuthenticationSuccessHandler $authenticationHandler)
{
$this->userService = $userService;
$this->authenticationHandler = $authenticationHandler;
}
public function __invoke(UserPasswordResetConfirmation $dto)
{
$user = $this->userService->confirmPasswordReset($dto);
$this->dispatch(UserEvent::PASSWORD_RESET_CONFIRMED, new UserEvent($user));
// creating a new JWT token
$response = $this->authenticationHandler->handleAuthenticationSuccess($user);
return $response;
}
}
Debugging showed that event is populated with response object vendor/api-platform/core/src/EventListener/WriteListener.php
at lines 60-66:
$persistResult = $this->dataPersister->persist($controllerResult);
//
$event->setControllerResult($persistResult ?? $controllerResult);
but later on somewhere controller result is overwritten with DTO object UserPasswordResetConfirmation
, than becomes a cause of an exception.