symfony2学习笔记(3)——URL路由

目录

一、创建路由

二、配置路由

第一种——注解方式

(1)普通注解

(2)增加前缀

第二种——配置文件方式(PHP/XML/YML)

(1)基础路由配置

(2)带占位符路由

(3)必需和可选占位符

  (4)添加要求约束

   (5)添加HTTP 方法约束

   (6)高级路由例子:

(7)特殊的路由参数:

  (8)Controller的命名模式:

   (9)路由参数和控制器参数

(10)包括外部路由资源

(11)给导入的路由资源添加前缀

三、可视化并调试路由

四、生成URL

(1)普通URL

(2)生成绝对路径的URL

(3)生成带有查询字符串的URL

(4)从模板中生成URL

五、强制路由使用HTTPS或者HTTP


Symfony支持配置路由的方式有多种,一种是直接在方法上面加Route注释,另一种是在配置文件中配置。配置文件分为yml/php/xml,这里我们只介绍yml格式。

一、创建路由


  Symfony会从一个单独的路由配置文件中加载你应用程序的所有路由。该文件通常为 app/config/routing.yml。 它也可以被配置成包括XML或者PHP文件等文件。

# app/config/config.yml
framework:
    # ...
    router:        { resource: "%kernel.root_dir%/config/routing.yml" }


二、配置路由


注解方式:

(1)普通注解

首先要在配置文件中配置一下:

# app/config/routing.yml
                 
# import routes from a controller directory
annot:
    resource: "@AnnotRoutingBundle/Controller"
    type:     annotation

注意其中的type为注解方式,如下例子很全的展示了注解配置:

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
                 
/**
 * @Route("/blog")
 * @Cache(expires="tomorrow")
 */
class AnnotController extends Controller
{
    /**
     * @Route("/")
     * @Template
     */
    public function indexAction()
    {
        $posts = ...;
                 
        return array('posts' => $posts);
    }
                 
    /**
     * @Route("/{id}")
     * @Method("GET")
     * @ParamConverter("post", class="SensioBlogBundle:Post")
     * @Template("SensioBlogBundle:Annot:show.html.twig", vars={"post"})
     * @Cache(smaxage="15", lastmodified="post.getUpdatedAt()", etag="'Post' ~ post.getId() ~ post.getUpdatedAt()")
     * @Security("has_role('ROLE_ADMIN') and is_granted('POST_SHOW', post)")
     */
    public function showAction(Post $post)
    {
    }
}

(2)增加前缀

如果我们建立一个apiController,他的地址统一为/api/testa、/api/testb、/api/testc,我们又不想在每个文件中配置,可以在配置文件中增加prefix:/api/,该controller中的所有方法前面都会增加/annot,

# app/config/routing.yml
                 
# import routes from a controller directory
annot:
    resource: "@AnnotRoutingBundle/Controller"
    type:     annotation
    prefix: /annot

PHP依然如第一步所写,想要访问到showAction就需要输入/api/23这样的路径。



在配置文件中配置:


(1)基础路由配置

  定义一个路由很简单,通常一个应用程序拥有很多路由。一个基础路由是由两部分组成:pattern部分和defaults数组部分。

比如:

YAML格式:

_welcome:
    pattern:   /
    defaults:  { _controller: AcmeDemoBundle:Main:homepage }

(2)带占位符路由

  当然,路由系统支持更多有趣的路由。许多路由会包含一个或者多个被命名的通配符占位符。

YAML格式:

blog_show:
    pattern:   /blog/{slug}
    defaults:  { _controller: AcmeBlogBundle:Blog:show }

该模式将匹配任何类似/blog/*形式的URL。匹配占位符{slug}的值将会在controller中被使用。换句话说,如果URL是/blog/hello-world,

则$slug变量值是hello-world, 该值将能在controller中被使用。该模式不会匹配像/blog, 因为默认情况下所有的占位符都是必须的。 当然可以通过在defaults数组中给这些占位符赋来改变它。

(3)必需和可选占位符

  我们来添加一个新的路由,显示所有可用的blog列表。

YAML格式:

blog:
    pattern:   /blog
    defaults:  { _controller: AcmeBlogBundle:Blog:index }

到目前为止,我们的路由都是非常简单的路由模式。它们包含的非占位符将会被精确匹配。


  如果你想该路由能够支持分页,比如让/blog/2 显示第二页的blog,那就需要为之前的路由添加一个新的{page}占位符。

YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index }

跟之前的{slug}占位符一样{page}占位符将会在你的controller内部可用,它的值可以用于表示要显示的blog值的页码。但是要清楚,因为占位符默认情况下都是必需的,该路由也将不再匹配之前的/blog URL,这时候你如果还像看第一页的话,就必须通过/blog/1 URL来访问了。要解决该问题,可以在该路由的defaults数组中指定{page}的默认值。

YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index, page: 1 }

通过添加page到defaults键, {page}占位符就不再是必需的。这时候 /blog将会被匹配并且page参数被设置为1,URL /blog/2 也会被匹配。


(4)添加要求约束

看看下面这些路由:

YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index, page: 1 }
blog_show:
    pattern:   /blog/{slug}
    defaults:  { _controller: AcmeBlogBundle:Blog:show }

你发现问题了吗?注意这两个路由都能匹配像/blog/* 类型的URL。Symfony只会选择第一个与之匹配的路由。

  换句话说,blog_show将永远不会被像/blog/* 类型的URL匹配。而像 /blog/my-blog-post这样的URL也会被blog路由匹配,并且page变量会获得my-blog-post这样的值。

  这肯定不可以,那么怎么办呢?答案是给路由添加约束要求requirements。

  在blog路由中占位符{page}理想状态下只匹配整数值。幸运的是正则表达可以很容易的满足这一要求。

YAML格式:

blog:
    pattern:   /blog/{page}
    defaults:  { _controller: AcmeBlogBundle:Blog:index, page: 1 }
    requirements:
        page:  d+

这里 d+ 约束是一个正则表达式,它指定了{page}只接受整数。这样像/blog/my-blog-post就不再被匹配了。这时候,它才会被blog_show路由匹配。因为参数的约束都是正则表达式,所以其复杂程度和灵活性都有你来决定了。


  假设home页使用两种语言则可以这样配置路由:

YAML格式:

homepage:
    pattern:   /{culture}
    defaults:  { _controller: AcmeDemoBundle:Main:homepage, culture: en }
    requirements:
        culture:  en|fr

(5)添加HTTP 方法约束

  除了URL,你还可以匹配请求的方法(GET,HEAD,POST,PUT,DELETE等)。假设你有一个联系表单有两个controller,一个用于显示表单(使用GET请求)一个用于处理提交的表单(POST请求)。它的配置如下:

YAML格式:

contact:
    pattern:  /contact
    defaults: { _controller: AcmeDemoBundle:Main:contact }
    requirements:
        _method:  GET
contact_process:
    pattern:  /contact
    defaults: { _controller: AcmeDemoBundle:Main:contactProcess }
    requirements:
        _method:  POST

尽管这两个路由拥有同一个URL模式定义(/contact),但是第一个路由只会匹配GET请求,而第二个只会匹配POST请求。这就意味着你可以通过同一个URL来显示表单并提交表单,而用不同的controller对他们进行处理。如果没有指定_method约束,那么该路由会匹配所有请求方法。跟其它约束一样,_method约束也接受正则表达式,如果只想匹配GET或者POST那么你可以用GET|POST


(6)高级路由例子:

Symfony2中具备一切让你创建任何形式路由的条件。

YAML格式:

article_show:
  pattern:  /articles/{culture}/{year}/{title}.{_format}
  defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
  requirements:
      culture:  en|fr
      _format:  html|rss
      year:     d+

上面的路由,在匹配时只会匹配{culture}部分值为en或者fr并且{year}的值为数字的URL。该路由还告诉我们,可以用在占位符之间使用区间代替斜线。


它能够匹配如下URL:

  /articles/en/2010/my-post

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

  这其中有个特殊的路由参数 _format,在使用该参数时,其值变为请求格式。这种请求格式相当于Respose对象的Content-Type,比如json请求格式会翻译成一个Content-Type为application/json.该参数可以用于在controller中为每个_format渲染一个不同的模板。它是一个很强的方式来渲染同一个内容到不同的格式。


(7)特殊的路由参数:

正如你所看到的,每一个路由参数或者默认值最终都是作为一个controller方法输入参数被使用。另外,有三个参数比较特别,它们每一个都在你的应用程序中增加一个唯一功能。

  _controller: 这个参数决定了当路由匹配时,哪个controller被执行。

  _format: 用于设置请求格式。

  _locale: 用于在session上设置本地化。

(8)Controller的命名模式:

每一个路由必须有一个_controller参数,它决定了当路由匹配时哪个controller应该被执行。该参数使用单一的字符串模式,被称为logical controller name。

通过它Symfony可以映射到一个特定的PHP方法和类。该模式有三部分,每一部分用冒号分割开:

bundle:controller:action

  比如_controller 的值为 AcmeBlogBundle:Blog:show 意思是AcmeBlogBundle bundle中BlogController类里面的showAction方法。

// src/Acme/BlogBundle/Controller/BlogController.php
               
namespace Acme\BlogBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
               
class BlogController extends Controller
{
    public function showAction($slug)
    {
        // ...
    }
}

Symfony会自动把它们的添加相应的后缀,Blog=>BlogController, show => showAction。

  你也可以使用它的完全限定名和方法来给_controller赋值,AcmeBlogBundleControllerBlogController::showAction 但一般为了简洁灵活而是用逻辑名称。另外除了上面两种形式外,Symfony还支持第三种方式只有一个冒号分割符,如service_name:indexAction来为_controller赋一个作为服务使用的controller。

(9)路由参数和控制器参数

  路由参数非常重要,因为每一个路由参数都会转变成一个控制器参数被在方法中使用。

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

事实上,全部的defaults集合和表单的参数值合并到一个单独的数组中。这个数组中的每个键都会成为controller方法的参数。换句话说,你的controller方法的每一个参数,Symfony都会从路由参数中查找并把找到的值赋给给参数。上面例子中的变量 $culture, $year,$title,$_format,$_controller 都会作为showAction()方法的参数。因为占位符和defaults集合被合并到一起,即使$_controller变量也是一样。你也可以使用一个特殊的变量$_route 来指定路由的名称。

(10)包括外部路由资源

所有的路由资源的都是通过一个单一的配置文件导入的。通常是app/config/routing.yml。当然你可能想从别处导入路由资源,比如你定义的bundle中的路由资源,你可以这样导入:

YAML格式:

# app/config/routing.yml
acme_hello:
    resource: "@AcmeHelloBundle/Resources/config/routing.yml"

在使用YAML导入资源时,键(比如acme_hello)是没有意义的,只是用来保证该资源唯一不被其它行覆盖。使用resources key加载给定的路由资源。在这个示例中资源是一个全路径文件,@AcmeHelloBundle是简写语法,它会被指向bundle路径。被导入的文件内容如下:

YAML格式:

# src/Acme/HelloBundle/Resources/config/routing.yml
acme_hello:
     pattern:  /hello/{name}
     defaults: { _controller: AcmeHelloBundle:Hello:index }

(11)给导入的路由资源添加前缀

你可以为导入的路由资源选择一个前缀,比如说假设你想acme_hello路由有一个这样的 匹配模式:/admin/hello/{name} 而不是直接的 /hello/{name}

那么你在导入它的时候可以为其指定prefix。

YAML格式:

# app/config/routing.yml
acme_hello:
    resource: "@AcmeHelloBundle/Resources/config/routing.yml"
    prefix:   /admin

三、可视化并调试路由


当你添加和个性化路由时,能够看到它并能获取一些细节信息将是非常有用的。一个好的查看你应用程序的路由的方法是通过router:debug 命令行工具。

在你项目目录下执行如下命令:

php app/console router:debug

将会输出你应用程序的所有路由。你也可以在该命令后面添加某个路由的名字来获取单个路由信息

php app/console router:debug article_show

你也可以使用match命令去匹配该路由


app/console router:match /article/new

四、生成URL

(1)普通URL

一个路由系统应该也能用来生成URL。事实上,路由系统是一个双向的系统,映射URL到controller+parameters 和 回对应到 一个URL。可以使用match()和generate()方法来操作。比如:

$params = $router->match('/blog/my-blog-post');
// array('slug' => 'my-blog-post', '_controller' => 'AcmeBlogBundle:Blog:show')
          
$uri = $router->generate('blog_show', array('slug' => 'my-blog-post'));
// /blog/my-blog-post

要生成一个URL,你需要指定路由的名称(比如 blog_show)和任意的通配符(比如:slug=my-blog-post)作为参数。通过这些信息,可以生成任意的URL。

class MainController extends Controller
{
    public function showAction($slug)
    {
      // ...
          
      $url = $this->get('router')->generate('blog_show', array('slug' => 'my-blog-post'));
    }
}

那么如何从模板内部来生成URL呢?

如果你的应用程序前端使用了AJAX请求,你也许想能够基于你的路由配置在javascript中生成URL,通过使用FOSJsRoutingBundle(https://github.com/FriendsOfSymfony/FOSJsRoutingBundle) 你可以做到:

var url = Routing.generate('blog_show', { "slug": 'my-blog-post'});

(2)生成绝对路径的URL

默认的情况下,路由器生成相对路径的URL(比如 /blog).要生成一个绝对路径的URL,只需要传入一个true到generate方法作为第三个参数值即可。

$router->generate('blog_show', array('slug' => 'my-blog-post'), true);
// http://www.example.com/blog/my-blog-post

当生成一个绝对路径URL时主机是当前请求对象的主机,这个是基于PHP提供的服务器信息自动决定的。当你需要为运行子命令行的脚本生成一个绝对URL时,你需要在Request对象上手动设置期望的主机头。

$request->headers->set('HOST', 'www.example.com');

(3)生成带有查询字符串的URL

generate()带有一个数组通配符值来生成URI。 但是如果你传入额外的值,它将被添加到URI作为查询字符串:

$router->generate('blog', array('page' => 2, 'category' => 'Symfony'));
// /blog/2?category=Symfony

(4)从模板中生成URL

最常用到生成URL的地方是从模板中链接两个页面时,这来需要使用一个模板帮助函数:

<a href="{{ path('blog_show', { 'slug': 'my-blog-post' }) }}">
  Read this blog post.
</a>

也可以生成绝对路径:

<a href="{{ url('blog_show', { 'slug': 'my-blog-post' }) }}">
  Read this blog post.
</a>


五、强制路由使用HTTPS或者HTTP


有时候为了安全起见,你需要你的站点必须使用HTTPS协议访问。这时候路由组件可以通过_scheme 约束来强迫URI方案。比如:

secure:
    pattern:  /secure
    defaults: { _controller: AcmeDemoBundle:Main:secure }
    requirements:
        _scheme:  https

上面的路由定义就是强迫secure路由使用HTTPS协议访问。

反之,当生成secure 的URL的时候,路由系统会根据当前的访问协议方案生成相应的访问协议。比如当前是HTTP,则会自动生成HTTPS访问;如果是HTTPS访问,那么就也会相应的生成HTTPS访问。

# 如果方案是 HTTPS
{{ path('secure') }}
# 生成 /secure
          
# 如果方案是 HTTP
{{ path('secure') }}
# 生成 https://example.com/secure

当然你也可以通过设置_scheme为HTTP,来强制使用HTTP访问协议。除了上面说的强迫使用HTTPS协议访问的设置方法外,还有一种用于站点区域设置。

使用requires_channel 比如你想让你站点中/admin 下面的所有路由都必须使用HTTPS协议访问,或者你的安全路由定义在第三方bundle时使用。


by 雪洁 2015-03-09 07:32:18 4848 views
我来说几句

相关文章