资讯专栏INFORMATION COLUMN

Symfony4中文文档: 路由

everfight / 3264人阅读

摘要:路由漂亮的是任何严谨的应用程序所必须的这意味着像这样丑陋的要被所取代具有灵活性更加重要如果你需要将更改为需要做些什么你需要搜索并更新多少链接才能做出这种改动如果你使用的是的路由更改将是很简单的创建路由路由是从到控制器的映射假如你想要一个

路由

漂亮的URL是任何严谨的Web应用程序所必须的. 这意味着像 index.php?article_id=57 这样丑陋的URL要被 /read/intro-to-symfony 所取代.

具有灵活性更加重要. 如果你需要将 /blog 更改为 /news , 需要做些什么? 你需要搜索并更新多少链接才能做出这种改动? 如果你使用的是Symfony的路由, 更改将是很简单的.

创建路由

路由是从URL到控制器的映射, 假如你想要一个路由完全匹配 /blog 和另外更多可匹配任何像 /blog/my-post/blog/all-about-symfony URL的动态路由.

路由可以在YAML, XML和PHP. 所有格式都提供相同的功能和性能, 因此可选择你喜欢的格式. 如果你选择PHP annotations, 请在你的应用程序中运行一次此命令以添加对它们的支持:

$ composer require annotations

现在你可以配置路由:

Annotations

// src/Controller/BlogController.php
namespace AppController;

use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentRoutingAnnotationRoute;

class BlogController extends AbstractController
{
    /**
     * Matches /blog exactly
     *
     * @Route("/blog", name="blog_list")
     */
    public function list()
    {
        // ...
    }

    /**
     * Matches /blog/*
     *
     * @Route("/blog/{slug}", name="blog_show")
     */
    public function show($slug)
    {
        // $slug will equal the dynamic part of the URL
        // e.g. at /blog/yay-routing, then $slug="yay-routing"

        // ...
    }
}

YAML

# config/routes.yaml
blog_list:
    path:     /blog
    controller: AppControllerBlogController::list

blog_show:
    path:     /blog/{slug}
    controller: AppControllerBlogController::show
    

XML





    
        
    

    
        
    

PHP

// config/routes.php
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use AppControllerBlogController;

$routes = new RouteCollection();
$routes->add("blog_list", new Route("/blog", array(
    "_controller" => [BlogController::class, "list"]
)));
$routes->add("blog_show", new Route("/blog/{slug}", array(
    "_controller" => [BlogController::class, "show"]
)));

return $routes;

感谢这两条路由:

如果用户访问 /blog , 匹配第一条路由配置并且 list() 将被执行;

如果用户访问 /blog/* , 匹配第二条路由配置并且 show() 将被执行. 因为路由路径是 /blog/{slug}, 所以 $slug 变量传递给该值匹配的 show() . 例如, 如果用户访问 /blog/yay-routing , 那么 $slug 将等于 yay-routing .

每当路由路径中有 {placeholder} 时, 该部分就成为通配符: 它将匹配任意值. 你的控制器现在也有一个名为 $placeholder 的参数 ( 通配符和参数名称必须匹配 ).

每个路由还有一个内部名称: blog_listblog_show . 这些可以是任意内容 ( 只要每个都是唯一的 ) 并且需要无任何特别含义. 稍后你将使用它们来生成URL.

其他格式的路由

每个方法上面的 @Route 称为 annotation. 如果你更愿意使用YAML, XML或PHP配置路由, 那没问题! 只需创建一个新的路由文件 ( 例如 routes.xml ) , Symfony就会自动使用它.

本地化路由(i18n)

路由可以本地化地为每个区域提供唯一的路径. Symfony提供了一种简便的方式来声明本地化路由而无重复.

Annotations

// src/Controller/CompanyController.php
namespace AppController;

use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentRoutingAnnotationRoute;

class CompanyController extends AbstractController
{
    /**
     * @Route({
     *     "nl": "/over-ons",
     *     "en": "/about-us"
     * }, name="about_us")
     */
    public function about()
    {
        // ...
    }
}

YAML

# config/routes.yaml
about_us:
    path:
        nl: /over-ons
        en: /about-us
    controller: AppControllerCompanyController::about
    

XML





    
        /over-ons
        /about-us
    

PHP

// config/routes.php
namespace SymfonyComponentRoutingLoaderConfigurator;

return function (RoutingConfigurator $routes) {
    $routes->add("about_us", ["nl" => "/over-ons", "en" => "/about-us"])
        ->controller("AppControllerCompanyController::about");
};

当本地化路由匹配时, Symfony会自动识别请求期间应使用哪个区域的路由设置. 以这种方式定义路由避免了对路由重复注册的需要, 最小化了由定义不一致引起的任何错误的风险.

为所有路由添加前缀是国际化应用程序的一个常见需求. 这样可以通过为每个语言环境定义不同的路径前缀来完成 ( 如果愿意, 可以为默认语言设置一个空前缀 ):

YAML

# config/routes/annotations.yaml
controllers:
    resource: "../../src/Controller/"
    type: annotation
    prefix:
        en: "" # don"t prefix URLs for English, the default locale
        nl: "/nl"
        
添加 {通配符} 条件

想象一下, blog_list 路由将包含一个博客主题的分页列表, 其中包含 /blog/2/blog/3 等第2页和第3页的URL. 如果你将路径修改为 /blog/{page} , 你将会遇到一个问题:

blog_list: /blog/{page} 将匹配 /blog/*;

blog_show: /blog/{slug} 将仍然匹配 /blog/*;

当两条路由匹配相同的URL时, 加载的第一条路由将胜利. 不幸的是, 这意味着 /blog/yay-routing 将匹配 blog_list.

要解决此问题, 添加一个 {page} 通配符用来只匹配数字:

Annotations

// src/Controller/BlogController.php
namespace AppController;

use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentRoutingAnnotationRoute;

class BlogController extends AbstractController
{
    /**
     * @Route("/blog/{page}", name="blog_list", requirements={"page"="d+"})
     */
    public function list($page)
    {
        // ...
    }

    /**
     * @Route("/blog/{slug}", name="blog_show")
     */
    public function show($slug)
    {
        // ...
    }
}

YAML

# config/routes.yaml
blog_list:
    path:      /blog/{page}
    controller: AppControllerBlogController::list
    requirements:
        page: "d+"

blog_show:
    # ...

XML





    
        d+
    

    

PHP

// config/routes.php
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use AppControllerBlogController;

$routes = new RouteCollection();
$routes->add("blog_list", new Route("/blog/{page}", array(
    "_controller" => [BlogController::class, "list"],
), array(
    "page" => "d+"
)));

// ...

return $routes;

d+ 是一个匹配任意长度数字的正则表达式. 现在:

URL Route Parameters
/blog/2 blog_list $page = 2
/blog/yay-routing blog_show $slug = yay-routing

如果你愿意, 可以在每个占位符中使用语法 {placeholder_name} . 此功能使配置更简洁, 但当需求复杂时, 它会降低路由可读性:

Annotations

// src/Controller/BlogController.php
namespace AppController;

use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentRoutingAnnotationRoute;

class BlogController extends AbstractController
{
    /**
     * @Route("/blog/{page}", name="blog_list")
     */
    public function list($page)
    {
        // ...
    }
}

YAML

# config/routes.yaml
blog_list:
    path:      /blog/{page}
    controller: AppControllerBlogController::list

XML





    

    

PHP

// config/routes.php
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use AppControllerBlogController;

$routes = new RouteCollection();
$routes->add("blog_list", new Route("/blog/{page}", array(
    "_controller" => [BlogController::class, "list"],
)));

// ...

return $routes;

要了解其他路由条件 ( 如HTTP方法, 主机名和动态表达式 ) 请参阅 How to Define Route Requirements

给{占位符}一个默认值

在前面的例子中, blog_list 的路径为 /blog/{page} . 如果用户访问 /blog/1 , 则会匹配. 如果用户访问 /blog , 将无法匹配. 只要向路由路径添加了 {占位符} , 它就必须有值.

那么当用户访问 /blog 时, 如何让 blog_list 再次匹配呢? 通过添加一个 默认 值:

Annotations

// src/Controller/BlogController.php
namespace AppController;

use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentRoutingAnnotationRoute;

class BlogController extends AbstractController
{
    /**
     * @Route("/blog/{page}", name="blog_list", requirements={"page"="d+"})
     */
    public function list($page = 1)
    {
        // ...
    }
}

YAML

# config/routes.yaml
blog_list:
    path:      /blog/{page}
    controller: AppControllerBlogController::list
    defaults:
        page: 1
    requirements:
        page: "d+"

blog_show:
    # ...
    

XML





    
        1

        d+
    

    

PHP

// config/routes.php
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use AppControllerBlogController;

$routes = new RouteCollection();
$routes->add("blog_list", new Route(
    "/blog/{page}",
    array(
        "_controller" => [BlogController::class, "list"],
        "page"        => 1,
    ),
    array(
        "page" => "d+"
    )
));

// ...

return $routes;

现在, 当用户访问 /blog 时, blog_list 路由会匹配, 并且 $page 路由参数会默认取值为 1 .

与{通配符}条件一样, 使用语法 {placeholder_name?default_value} 也可以在每个占位符中内联默认值. 此功能与内联条件兼容, 因此你可以在一个占位符中内联:

Annotations

// src/Controller/BlogController.php
namespace AppController;

use SymfonyBundleFrameworkBundleControllerAbstractController;
use SymfonyComponentRoutingAnnotationRoute;

class BlogController extends AbstractController
{
    /**
     * @Route("/blog/{page?1}", name="blog_list")
     */
    public function list($page)
    {
        // ...
    }
}

YAML

# config/routes.yaml
blog_list:
    path:      /blog/{page?1}
    controller: AppControllerBlogController::list

XML





    

    

PHP

// config/routes.php
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use AppControllerBlogController;

$routes = new RouteCollection();
$routes->add("blog_list", new Route("/blog/{page?1}", array(
    "_controller" => [BlogController::class, "list"],
)));

// ...

return $routes;
占位符变量的值若是 null 变量, 则需要在通配符最后添加 ? 字符. ( 例如 /blog/{page?} ) .
全部路由列表

随着你应用程序的健壮, 最终会有大量的路由被定义! 要查看所有内容, 请运行命令:

$ php bin/console debug:router

------------------------------ -------- -------------------------------------
 Name                           Method   Path
------------------------------ -------- -------------------------------------
 app_lucky_number                 ANY    /lucky/number/{max}
 ...
------------------------------ -------- -------------------------------------
高级路由示例

请查看高级示例:

Annotations

// src/Controller/ArticleController.php

// ...
class ArticleController extends AbstractController
{
    /**
     * @Route(
     *     "/articles/{_locale}/{year}/{slug}.{_format}",
     *     defaults={"_format": "html"},
     *     requirements={
     *         "_locale": "en|fr",
     *         "_format": "html|rss",
     *         "year": "d+"
     *     }
     * )
     */
    public function show($_locale, $year, $slug)
    {
    }
}

YAML

# config/routes.yaml
article_show:
  path:     /articles/{_locale}/{year}/{slug}.{_format}
  controller: AppControllerArticleController::show
  defaults:
      _format: html
  requirements:
      _locale:  en|fr
      _format:  html|rss
      year:     d+

XML





    

        html
        en|fr
        html|rss
        d+

    

PHP

// config/routes.php
use SymfonyComponentRoutingRouteCollection;
use SymfonyComponentRoutingRoute;
use AppControllerArticleController;

$routes = new RouteCollection();
$routes->add(
    "article_show",
    new Route("/articles/{_locale}/{year}/{slug}.{_format}", array(
        "_controller" => [ArticleController::class, "show"],
        "_format"     => "html",
    ), array(
        "_locale" => "en|fr",
        "_format" => "html|rss",
        "year"    => "d+",
    ))
);

return $routes;

如你所见, 只有当URL的 {_locale} 部分为 enfr{year} 为数字时, 此路由才会匹配. 示例还展示了如何在占位符之间使用 . 号来替换 / . 以下URL都可匹配:

/articles/en/2010/my-post

/articles/fr/2010/my-post.rss

/articles/en/2013/my-latest-post.html

_format 路由参数

示例突出显示了 _format 特殊路由参数, 当使用此参数时, 匹配的值将成为Request对象的"请求格式".

最后, 请求格式被用作设置返回 Content-Type 之类的事情 ( 例如: 一个JSON请求格式会转换 Content-Typeapplication/json )

特殊路由参数

如你所见, 每个路由参数或默认值最终都可以作为控制器方法的参数. 此外, 还有四个特殊参数: 每个参数在应用程序中具有独特的功能:

_controller

   用于确定路由匹配时执行的控制器

_format

   用于设置请求格式 ( 阅读更多 )

_fragment

   用于设置fragment identifier, URL的最后可选部分, 以 # 字符开头, 用于标识文档的某一部分.

_locale

   用于在请求上设置区域 ( 阅读更多 )

尾部斜杠重定向URL

从历史上看, URL遵循UNIX约定, 即为路径添加尾部斜杠 ( 例如 https://example.com/foo/ ) , 当删除斜杠时将作为文件引用 ( https://example.com/foo ) . 虽然为两个URL提供不同的内容是可以的, 但现在将两个URL视为相同的URL并在他们之间重定向是很常见的.

Symfony遵循这个逻辑, 在带斜杠和不带斜杠的URL之间重定向 ( 但仅限于GET和HEAD请求 ):

Route path If the requested URL is /foo If the requested URL is /foo/
/foo It matches (200 status response) It makes a 301 redirect to /foo
/foo/ It makes a 301 redirect to /foo/ It matches (200 status response)
如果你的应用程序为每个路径 ( /foo/foo/ ) 定义了不同的路由, 则不会发生自动重定向, 并且始终匹配正确的路由.

在Symfony4.1中引入了从 /foo//foo 的自动301重定向. 在之前的Symfony版本中, 会响应404.

控制器命名模式

路由中的控制器格式非常简单 CONTROLLER_CLASS::METHOD .

To refer to an action that is implemented as the __invoke() method of a controller class, you do not have to pass the method name, but can just use the fully qualified class name (e.g. AppControllerBlogController).
生成URL

路由系统也可以生成URL. 实际上, 路由是双向系统: 将URL映射到控制器以及路由返解为URL.

要生成URL, 你需要制定路由的名称 ( 例如 blog_show ) 以及该路由的路径中使用的任何通配符 ( 例如 slug = my-blog-post ) . 有了这些信息, 可轻松生成任何URL:

class MainController extends AbstractController
{
    public function show($slug)
    {
        // ...

        // /blog/my-blog-post
        $url = $this->generateUrl(
            "blog_show",
            array("slug" => "my-blog-post")
        );
    }
}

如果需要从服务生成URL, 注入 UrlGeneratorInterface 服务.

// src/Service/SomeService.php

use SymfonyComponentRoutingGeneratorUrlGeneratorInterface;

class SomeService
{
    private $router;

    public function __construct(UrlGeneratorInterface $router)
    {
        $this->router = $router;
    }

    public function someMethod()
    {
        $url = $this->router->generate(
            "blog_show",
            array("slug" => "my-blog-post")
        );
        // ...
    }
}
使用查询字符串生成URL

generate() 方法采用通配符数组来生成URI. 但是如果你传递额外值, 他们将作为查询字符串添加到URI中.

$this->router->generate("blog", array(
    "page" => 2,
    "category" => "Symfony",
));
// /blog/2?category=Symfony
生成本地化URL

路由本地化时, Symfony默认使用当前请求区域来生成URL. 为了生成不同语言环境的URL, 你必须在parameters数组中传递 _locale :

$this->router->generate("about_us", array(
    "_locale" => "nl",
));
// generates: /over-ons
从模板中生成URL

要在Twig中生成URL: 请参阅模板章节. 如果你需要在JavaScript中生成URL, 请参阅 How to Generate Routing URLs in JavaScript

生成绝对URL

默认情况下, 路由将生成相对URL ( 例如 /blog ) . 在控制器中, 将 UrlGeneratorInterface::ABSOLUTE_URL 传递给 generateUrl() 方法的第三个参数:

use SymfonyComponentRoutingGeneratorUrlGeneratorInterface;

$this->generateUrl("blog_show", array("slug" => "my-blog-post"), UrlGeneratorInterface::ABSOLUTE_URL);
// http://www.example.com/blog/my-blog-post
The host that"s used when generating an absolute URL is automatically detected using the current Request object. When generating absolute URLs from outside the web context (for instance in a console command) this doesn"t work. See How to Generate URLs from the Console to learn how to solve this problem.
错误排除

以下是使用路由时可能会遇到的一些常见错误:

Controller "AppControllerBlogController::show()" requires that you provide a value for the "$slug" argument.

当你的控制器方法有一个参数 ( 例如 $slug ) 时会发生这种情况:

public function show($slug)
{
    // ..
}

你的路由没有 {slug} 通配符 ( 例如 /blog/show ). 在你的路由路径中增加 {slug} : /blog/show/{slug} 或为参数设置一个默认值 ( 例如 $slug = null )

Some mandatory parameters are missing ("slug") to generate a URL for route "blog_show".

这意味着你正在尝试生成 blog_show 路由的URL, 但你没有传递 slug 值 (这是必须的, 因为在路由路径中有一个 {slug} 通配符). 要解决此问题, 请在生成路由时传递 slug 值:

$this->generateUrl("blog_show", array("slug" => "slug-value"));

// or, in Twig
// {{ path("blog_show", {"slug": "slug-value"}) }}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29721.html

相关文章

  • Symfony4中文文档: 创建你的第一个Symfony页面

    摘要:创建你的第一个页面创建一个新页面无论是页面还是端点分为两步创建一个路由路由是一个指向你的页面比如同时它映射到一个控制器创建一个控制器控制器是你为了构造页面而写的功能获取传入的请求信息并用它创建一个对象该对象可以包含内容字符串甚至是图像或 创建你的第一个Symfony页面 创建一个新页面 - 无论是HTML页面还是JSON端点 - 分为两步: 创建一个路由: 路由(route)是一个指...

    AlanKeene 评论0 收藏0
  • Symfony4中文文档: 安装和设置Symfony框架

    摘要:安装和设置框架要创建新的应用程序首先确保使用的是或更高版本并且已经安装如果未安装请首先在系统上全局安装如果你想使用虚拟机请查看通过运行以下命令来创建新项目这将创建一个新的目录下载所需的依赖,甚至生成你所需的基本目录和文件换句话说你的 安装和设置Symfony框架 要创建新的Symfony应用程序, 首先确保使用的是PHP7.1 或更高版本并且已经安装Componser. 如果未安装, ...

    jsummer 评论0 收藏0
  • Koa v2.x 中文文档 Koa 对比 Express

    摘要:使用承诺和异步功能来摆脱回调地狱的应用程序,并简化错误处理。它暴露了自己的和对象,而不是的和对象。因此,可被视为的模块的抽象,其中是的应用程序框架。这使得中间件对于整个堆栈而言不仅仅是最终应用程序代码,而且更易于书写,并更不容易出错。 Koa 与 Express 此系列文章的应用示例已发布于 GitHub: koa-docs-Zh-CN. 可以 Fork 帮助改进或 Star 关注更新...

    summerpxy 评论0 收藏0
  • sanic中文文档

    摘要:入门指南路由路由允许用户为不同的端点指定处理程序函数。被访问服务器的基本,最终被路由器匹配到处理程序函数,测试,然后返回一个对象。请求参数将作为关键字参数传递给路线处理程序函数。例如所有有效的参数必须传递给以便构建一个。 入门指南 Install Sanic:python3 -m pip install sanicexample from sanic import Sanic from...

    琛h。 评论0 收藏0
  • Koa v2.x 中文文档 常见问题

    摘要:常见问题此系列文章的应用示例已发布于可以帮助改进或关注更新欢迎替代它更像是,但是很多的好东西被转移到的中间件级别,以帮助形成更强大的基础。这使得中间件对于整个堆栈而言不仅仅是最终应用程序代码,而且更易于书写,并更不容易出错。 常见问题 此系列文章的应用示例已发布于 GitHub: koa-docs-Zh-CN. 可以 Fork 帮助改进或 Star 关注更新. 欢迎 Star. Koa...

    Paul_King 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<