如何从 Early/Auto 加载的 Symfony 3 路由器生成绝对 URL

How to generate Absolute URL from Early/Auto loaded Symfony 3 router

我正在编写 Guards 来处理我的 Symfony 3 应用程序的 OAuth。

作为其中的一部分,在我的一项服务中,我需要生成一个绝对 URL 作为回调 URL 发送到 Twitter。

#services.yml
...
app.twitter_client:
    class: MyApiBundle\Services\TwitterClient
    arguments:
        - %twitter_client_id%
        - %twitter_client_secret%
        - connect_twitter_check
        - '@request_stack'
        - '@router'
        - '@logger'
app.twitter_authenticator:
    class: MyApiBundle\Security\TwitterAuthenticator
    arguments:
        - '@app.twitter_client'
        - '@logger'

记录器是临时调试用的。

#TwitterClient.php service
...
use Monolog\Logger;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RouterInterface;
class TwitterClient
{

/**
 * TwitterClient constructor.
 * @param string          $identifier
 * @param string          $secret
 * @param string          $callbackUrl
 * @param RequestStack    $requestStack
 * @param RouterInterface $router
 * @param Logger          $logger
 */
public function __construct(
    string $identifier,
    string $secret,
    string $callbackUrl,
    RequestStack $requestStack,
    RouterInterface $router,
    Logger $logger
) {
    $callbackUrl = $router->generate($callbackUrl, [], RouterInterface::ABSOLUTE_URL);
    $logger->info($callbackUrl);
    ...
}

当它向日志输出 $callbackUrl 时,它只是说本地主机,即使我正在使用不同的 URL.

访问它
http://localhost/connect/twitter/check

但是如果我从我的控制器上做同样的事情,它会输出完整的 URL。

#TwitterController.php
...
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;


class TwitterController extends Controller
{
    /**
     * @Route("/connect/twitter")
     * @return RedirectResponse
     */
    public function connectAction()
    {
        /** @var RouterInterface $router */
        $router = $this->container->get('router');
        $callbackUrl = $router->generate('connect_twitter_check', [], RouterInterface::ABSOLUTE_URL);
        /** @var Logger $logger */
        $logger = $this->container->get('logger');
        $this->container->get('logger')
            ->info($callbackUrl);
        ...

这输出:

https://dev.myapi.com:8082/app_dev.php/connect/twitter/check

dev.myapi.com域在我的主机文件中设置并指向本地主机,以便更容易区分我的本地应用程序,也更容易与 OAuth 服务集成。

我设法通过设置路由器上下文解决了这个问题。 我已经加载了 RequestStack 来访问用户的 IP,所以我用它来设置路由的上下文,然后路由器按预期工作。 我 运行 进入 problems 试图在构造函数中使用 RequestStack,所以我最终重构了它,以便它尽可能晚地使用 RequestStack。

class TwitterClient
{
    private $identifier;
    private $secret;
    private $callbackUrl;
    private $requestStack;
    private $router;
    private $server;

    /**
     * TwitterClient constructor.
     *
     * @param string          $identifier
     * @param string          $secret
     * @param string          $callbackUrl
     * @param RequestStack    $requestStack
     * @param RouterInterface $router
     */
    public function __construct(
        string $identifier,
        string $secret,
        string $callbackUrl,
        RequestStack $requestStack,
        RouterInterface $router
    ) {
        $this->identifier = $identifier;
        $this->secret = $secret;
        $this->callbackUrl = $callbackUrl;
        $this->requestStack = $requestStack;
        $this->router = $router;
    }

    /**
     * Wait until the last moment to create the Twitter object to make sure that the requestStack is loaded.
     *
     * @return \League\OAuth1\Client\Server\Twitter
     */
    private function getServer()
    {
        if (!$this->server) {
            $context = new RequestContext();
            $context->fromRequest($this->requestStack->getCurrentRequest());
            $this->router->setContext($context);
            $callbackUrl = $this->router->generate($this->callbackUrl, [], RouterInterface::ABSOLUTE_URL);
            $this->server = new Twitter(
                [
                    'identifier' => $this->identifier,
                    'secret' => $this->secret,
                    'callback_uri' => $callbackUrl,
                ]
            );
        }

        return $this->server;
    }
    ...
}