路由高级 #

一、URL生成 #

1.1 控制器中生成URL #

php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class LinkController extends AbstractController
{
    #[Route('/links', name: 'app_links')]
    public function links(): Response
    {
        $url = $this->generateUrl('app_user_show', ['id' => 123]);
        
        return new Response("<a href=\"$url\">User 123</a>");
    }

    #[Route('/user/{id}', name: 'app_user_show')]
    public function show(int $id): Response
    {
        return new Response("User ID: $id");
    }
}

1.2 生成绝对URL #

php
<?php

class UrlController extends AbstractController
{
    #[Route('/url-test', name: 'app_url_test')]
    public function test(): Response
    {
        $relativeUrl = $this->generateUrl('app_user_show', ['id' => 1]);
        $absoluteUrl = $this->generateUrl('app_user_show', ['id' => 1], UrlGeneratorInterface::ABSOLUTE_URL);
        
        return new Response("
            Relative: $relativeUrl<br>
            Absolute: $absoluteUrl
        ");
    }
}

1.3 使用Router服务 #

php
<?php

use Symfony\Component\Routing\RouterInterface;

class UrlService
{
    public function __construct(
        private RouterInterface $router
    ) {}

    public function generateUserUrl(int $id): string
    {
        return $this->router->generate('app_user_show', ['id' => $id]);
    }

    public function generateAbsoluteUrl(int $id): string
    {
        return $this->router->generate(
            'app_user_show',
            ['id' => $id],
            RouterInterface::ABSOLUTE_URL
        );
    }
}

1.4 Twig模板中生成URL #

twig
{# 相对URL #}
<a href="{{ path('app_user_show', {id: user.id}) }}">查看用户</a>

{# 绝对URL #}
<a href="{{ url('app_user_show', {id: user.id}) }}">查看用户</a>

{# 带查询参数 #}
<a href="{{ path('app_search', {q: 'symfony', page: 1}) }}">搜索</a>

{# 当前URL #}
<p>当前URL: {{ app.request.uri }}</p>

{# 检查当前路由 #}
{% if app.request.attributes.get('_route') == 'app_home' %}
    当前是首页
{% endif %}

二、路由条件 #

2.1 请求方法条件 #

php
<?php

class ApiController extends AbstractController
{
    #[Route('/api/users', name: 'api_users_get', methods: ['GET'])]
    public function getUsers(): Response
    {
        return $this->json(['users' => []]);
    }

    #[Route('/api/users', name: 'api_users_post', methods: ['POST'])]
    public function createUser(): Response
    {
        return $this->json(['status' => 'created'], 201);
    }

    #[Route('/api/users/{id}', name: 'api_users_put', methods: ['PUT'])]
    public function updateUser(int $id): Response
    {
        return $this->json(['status' => 'updated']);
    }

    #[Route('/api/users/{id}', name: 'api_users_delete', methods: ['DELETE'])]
    public function deleteUser(int $id): Response
    {
        return $this->json(['status' => 'deleted']);
    }
}

2.2 域名条件 #

php
<?php

#[Route('/api', host: 'api.example.com')]
class ApiController extends AbstractController
{
    #[Route('/users', name: 'api_users')]
    public function users(): Response
    {
        return $this->json(['users' => []]);
    }
}

#[Route('/admin', host: '{subdomain}.example.com', requirements: ['subdomain' => 'admin|manage'])]
class AdminController extends AbstractController
{
    #[Route('/dashboard', name: 'admin_dashboard')]
    public function dashboard(string $subdomain): Response
    {
        return new Response("Admin Dashboard on $subdomain.example.com");
    }
}

2.3 协议条件 #

php
<?php

#[Route('/secure', schemes: ['https'])]
class SecureController extends AbstractController
{
    #[Route('/payment', name: 'secure_payment')]
    public function payment(): Response
    {
        return new Response('Secure Payment Page');
    }
}

2.4 表达式条件 #

php
<?php

use Symfony\Component\ExpressionLanguage\Expression;

class ConditionalController extends AbstractController
{
    #[Route(
        '/api/internal',
        name: 'api_internal',
        condition: "request.headers.get('X-API-Key') === 'secret'"
    )]
    public function internal(): Response
    {
        return $this->json(['status' => 'ok']);
    }

    #[Route(
        '/admin/impersonate',
        name: 'admin_impersonate',
        condition: "is_granted('ROLE_ADMIN')"
    )]
    public function impersonate(): Response
    {
        return new Response('Impersonate User');
    }

    #[Route(
        '/mobile',
        name: 'mobile_home',
        condition: "request.headers.get('User-Agent') matches '/mobile/i'"
    )]
    public function mobile(): Response
    {
        return new Response('Mobile Home');
    }
}

2.5 YAML条件配置 #

yaml
# config/routes.yaml

api_routes:
    resource:
        path: ../src/Controller/Api/
        namespace: App\Controller\Api
    type: attribute
    prefix: /api
    host: api.example.com
    schemes: [https]
    condition: "request.headers.get('X-API-Key') === 'secret'"
    methods: [GET, POST]

三、路由重定向 #

3.1 控制器重定向 #

php
<?php

class RedirectController extends AbstractController
{
    #[Route('/old-page', name: 'app_old_page')]
    public function oldPage(): Response
    {
        return $this->redirectToRoute('app_new_page');
    }

    #[Route('/new-page', name: 'app_new_page')]
    public function newPage(): Response
    {
        return new Response('New Page');
    }

    #[Route('/redirect-with-params', name: 'app_redirect_params')]
    public function redirectWithParams(): Response
    {
        return $this->redirectToRoute('app_user_show', ['id' => 123]);
    }

    #[Route('/redirect-permanent', name: 'app_redirect_permanent')]
    public function redirectPermanent(): Response
    {
        return $this->redirectToRoute('app_new_page', [], 301);
    }

    #[Route('/redirect-external', name: 'app_redirect_external')]
    public function redirectExternal(): Response
    {
        return $this->redirect('https://symfony.com');
    }
}

3.2 YAML重定向配置 #

yaml
# config/routes.yaml

# 简单重定向
old_blog:
    path: /old-blog
    controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
    defaults:
        path: /blog
        permanent: true

# 带参数重定向
old_user_profile:
    path: /user/{id}/profile
    controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction
    defaults:
        route: app_user_show
        parameters:
            id: $id
        permanent: false

3.3 路由级别重定向 #

php
<?php

use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

$routes = new RouteCollection();

$routes->add('old_contact', new Route('/contact-us', [
    '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction',
    'path' => '/contact',
    'permanent' => true,
]));

$routes->add('contact', new Route('/contact', [
    '_controller' => 'App\Controller\ContactController::index',
]));

四、URL匹配 #

4.1 手动匹配URL #

php
<?php

use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;

class UrlMatcherService
{
    public function __construct(
        private UrlMatcher $urlMatcher
    ) {}

    public function matchUrl(string $url): array
    {
        try {
            $parameters = $this->urlMatcher->match($url);
            return [
                'route' => $parameters['_route'],
                'controller' => $parameters['_controller'],
                'params' => array_filter($parameters, fn($key) => !str_starts_with($key, '_'), ARRAY_FILTER_USE_KEY),
            ];
        } catch (\Exception $e) {
            return ['error' => $e->getMessage()];
        }
    }
}

4.2 命令行测试匹配 #

bash
# 测试URL匹配
php bin/console router:match /user/123

# 输出示例
[OK] Route "app_user_show" matches

+--------------+---------------------------------------------------------+
| Property     | Value                                                   |
+--------------+---------------------------------------------------------+
| Route Name   | app_user_show                                           |
| Path         | /user/{id}                                              |
| Parameters   | id: 123                                                 |
+--------------+---------------------------------------------------------+

五、自定义路由加载器 #

5.1 创建路由加载器 #

php
<?php

namespace App\Routing;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class CustomRouteLoader extends Loader
{
    private bool $loaded = false;

    public function load(mixed $resource, ?string $type = null): RouteCollection
    {
        if ($this->loaded) {
            throw new \RuntimeException('Do not add this loader twice');
        }

        $routes = new RouteCollection();

        $routes->add('custom_home', new Route('/custom', [
            '_controller' => 'App\Controller\CustomController::index',
        ]));

        $routes->add('custom_about', new Route('/custom/about', [
            '_controller' => 'App\Controller\CustomController::about',
        ]));

        $this->loaded = true;

        return $routes;
    }

    public function supports(mixed $resource, ?string $type = null): bool
    {
        return $type === 'custom';
    }
}

5.2 注册路由加载器 #

yaml
# config/services.yaml

services:
    App\Routing\CustomRouteLoader:
        tags: [routing.loader]

5.3 使用自定义加载器 #

yaml
# config/routes.yaml

custom_routes:
    resource: .
    type: custom

六、路由事件 #

6.1 路由事件监听 #

php
<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class RouteSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => [['onKernelRequest', 20]],
        ];
    }

    public function onKernelRequest(RequestEvent $event): void
    {
        $request = $event->getRequest();
        $route = $request->attributes->get('_route');

        if ($route === 'app_special') {
            $request->attributes->set('custom_param', 'value');
        }
    }
}

6.2 动态修改路由参数 #

php
<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class LocaleSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => [['setLocale', 20]],
        ];
    }

    public function setLocale(RequestEvent $event): void
    {
        $request = $event->getRequest();
        
        if ($locale = $request->attributes->get('_locale')) {
            $request->setLocale($locale);
        }
    }
}

七、路由性能优化 #

7.1 缓存路由 #

bash
# 生产环境预热缓存
php bin/console cache:warmup --env=prod

# 路由匹配使用缓存,无需额外配置

7.2 路由优化建议 #

text
路由优化建议:
├── 使用静态路由优先
├── 避免过多动态参数
├── 合理使用路由约束
├── 使用路由前缀分组
├── 避免复杂的条件表达式
└── 合理设置路由优先级

八、路由调试 #

8.1 调试命令 #

bash
# 查看所有路由
php bin/console debug:router

# 查看特定路由
php bin/console debug:router app_user_show

# 测试URL匹配
php bin/console router:match /user/123

# 查看路由缓存
php bin/console cache:pool:cache:clear cache.router

8.2 路由信息 #

bash
# 详细路由信息
php bin/console debug:router app_user_show --show-controllers

# 输出示例
+--------------+---------------------------------------------------------+
| Property     | Value                                                   |
+--------------+---------------------------------------------------------+
| Route Name   | app_user_show                                           |
| Path         | /user/{id}                                              |
| Path Regex   | #^/user/(?P<id>\d+)$#sD                                 |
| Host         | ANY                                                     |
| Host Regex   |                                                         |
| Scheme       | ANY                                                     |
| Method       | GET                                                     |
| Requirements | id: \d+                                                 |
| Class        | Symfony\Component\Routing\Route                         |
| Defaults     | _controller: App\Controller\UserController::show       |
| Options      | compiler_class: Symfony\Component\Routing\RouteCompiler |
+--------------+---------------------------------------------------------+

九、总结 #

本章学习了:

  • URL生成方法
  • 路由条件配置
  • 路由重定向
  • URL匹配
  • 自定义路由加载器
  • 路由事件监听
  • 路由性能优化
  • 路由调试命令

下一章将学习 控制器基础

最后更新:2026-03-28