我在 Drupal 8 中遇到标志模块的问题(csrf_token' URL 查询参数无效)

Ari*_*rif 5 php drupal module drupal-8 csrf-token

我生成了标志链接

  $flag_link = [
  '#lazy_builder' => ['flag.link_builder:build', [
    $product->getEntityTypeId(),
    $product->id(),
    'product_like',
  ]],
   '#create_placeholder' => TRUE,
];
Run Code Online (Sandbox Code Playgroud)

标志链接生成成功。但是当我点击标志链接时,我收到了错误消息作为响应

{message: "'csrf_token' URL query argument is invalid."}
message: "'csrf_token' URL query argument is invalid."
Run Code Online (Sandbox Code Playgroud)

Mar*_*ons 3

我找到了一个临时解决方案。不确定这是否是 Flag 中的错误,需要由模块维护者解决,或者是否按预期工作,因为这是 REST 响应,而不是视图或显示模式的典型 Drupal 调用。

在 ModuleRestResource.php 文件中(在我的例子中,ModuleRestResource.php 文件位于:{{DRUPAL_ROOT}}/web/modules/custom/{{Module_Name}}/src/Plugin/rest/resource/{{Module_Name} }RestResource.php):

use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\EntityInterface;
use Drupal\flag\FlagService;
use Drupal\Core\Render\RenderContext;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class ModuleRestResource extends ResourceBase {

  /**
   * A current user instance.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * @var $entityTypeManager \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;

  /**
   * @var \Drupal\flag\FlagService
   */
  protected $flagService;

  /**
   * @var Drupal\Core\Access\CsrfTokenGenerator
   */
  protected $csrfService;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->logger = $container->get('logger.factory')->get('module');
    $instance->currentUser = $container->get('current_user');
    $instance->entityTypeManager = $container->get('entity_type.manager');
    $instance->flagService = $container->get('flag');
    $instance->csrfService = $container->get('csrf_token');
    return $instance;
  }

  /**
   * Responds to GET requests.
   *
   * @param string $payload
   *
   * @return \Drupal\rest\ResourceResponse
   *   The HTTP response object.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
   *   Throws exception expected.
   */
  public function get($payload) {

    // You must to implement the logic of your REST Resource here.
    // Use current user after pass authentication to validate access.
    if (!$this->currentUser->hasPermission('access content')) {
      throw new AccessDeniedHttpException();
    }

    if (!is_numeric($payload)) {
      throw new BadRequestHttpException();
    }

    /*
     * This is the object that will be returned with the node details.
     */
    $obj = new \stdClass();

    // First load our node.
    /**
     * @var \Drupal\Core\Entity\EntityInterface
     */
    $node = $this->entityTypeManager->getStorage('node')->load($payload);

    /**
     * FIX STARTS HERE !!!!!
     */

    /**
     * Because we are rending code early in the process, we need to wrap in executeInRenderContext
     */

    $render_context = new RenderContext();
    $fl = \Drupal::service('renderer')->executeInRenderContext($render_context, function() use ($node, $payload) {

      /**
       * Get the flag we need and check if the selected node has been flagged by the current user
       *
       * Set the path to create a token. This is the value that is missing by default that creates an
       * invalid CSRF Token. Important to note that the leading slash should be left off for token generation
       * and then added to to the links href attribute
       *
       */
      $flag = $this->flagService->getFlagById('bookmark');
      $is_flagged = (bool) $this->flagService->getEntityFlaggings($flag, $node, \Drupal::currentUser() );
      $path = 'flag/'. ($is_flagged ? 'un' : '') .'flag/bookmark/' . $node->id();
      $token = $this->csrfService->get($path);
      $flag_link = $flag->getLinkTypePlugin()->getAsFlagLink($flag, $node);
      $flag_link['#attributes']['href'] = '/' . $path . '?destination&token=' . $token;

      /**
       * Render the link into HTML
       */
      return \Drupal::service('renderer')->render($flag_link);
    });

    /**
     * This is required to bubble metadata
     */
    if (!$render_context->isEmpty()) {
        $bubbleable_metadata = $render_context->pop();
        \Drupal\Core\Render\BubbleableMetadata::createFromObject($fl)
            ->merge($bubbleable_metadata);
    }
    /*
     * !!!!! FIX ENDS HERE !!!!!
     */

    $obj->flag_link = $fl;

    return new ResourceResponse((array)$obj, 200);
  }

}
Run Code Online (Sandbox Code Playgroud)

任何能让模块维护者解决这个问题的人都会很好。