MVC 路由如何工作?

How does MVC routing work?

提问人:Madara's Ghost 提问时间:9/15/2012 最后编辑:PeeHaaMadara's Ghost 更新时间:5/7/2019 访问量:34808

问:

因此,我开始更深入地研究 MVC(真正的 MVC,而不是框架 MVC),并且我正在尝试开发一个小框架。我正在阅读其他框架,如 Symphony 和 Zend,看看它们是如何完成的,并尝试自己实现它。

我卡住的地方是 URL 路由系统:

<?php
namespace Application\Common;

class RouteBuilder {

    public function create($name, $parameters) {
        $route           = new Route($name);
        $route->resource = array_keys($parameters)[0];
        $route->defaults = $parameters["defaults"];
        $notation        = $parameters["notation"];
        $notation = preg_replace("/\[(.*)\]/", "(:?$1)?", $notation);
        foreach ($parameters["conditions"] as $param => $condition) {
            $notation = \str_replace($param, $condition, $notation);
        }

        $notation = preg_replace("/:([a-z]+)/i", "(?P<$1>[^/.,;?\n]+)", $notation);

        //@TODO: Continue pattern replacement!!
    }
}
/* How a single entry looks like
 * "main": {
    "notation": "/:action",
    "defaults": {
        "resource"  :   "Authentication",
    },
    "conditions":   {
        ":action"   :   "(login)|(register)"
    }
},

 */

我只是无法正确地将我的头包裹在它身上。从这里开始的应用程序工作流程是什么?

模式生成了,可能是要放在对象下面的对象什么的,然后呢?它是如何工作的?RouteRequest

附言在这里寻找一个真实的、解释清楚的答案。我真的很想了解这个主题。如果有人花时间写一个真正详细的答案,我将不胜感激。

php model-view-controller 路由

评论

0赞 JvdBerg 9/15/2012
看看我的博客: www.ganymedes.be
0赞 Madara's Ghost 9/15/2012
@JvdBerg:我在那里看不到任何关于路由本身的信息,你有更具体的链接吗?还有一个提示,在链接前面加上 http://,以便它们被解析,这样你就可以获得不错的SEO奖励:P
0赞 Mārtiņš Briedis 9/15/2012
为什么不看看现有的框架,例如Kohana或Fuel?
0赞 JvdBerg 9/15/2012
我从我的框架中发布了路由器类。希望它有所帮助。
0赞 Madara's Ghost 9/15/2012
@MrtiBriedis:我有,请阅读问题。我仍然没有得到它(如果我没有用尽所有其他手段,我不会在这里问。

答:

31赞 w00 9/15/2012 #1

MVC 类(它是更广泛的 Front Controller 的一部分)分解 HTTP 请求的 URL,特别是路径组件(可能还有查询字符串)。Router

尝试将路径组件的前一个或两个部分与相应的路由组合 ( / Action [method] 匹配,或者只是执行默认操作method) 的路由组合。RouterControllerController

操作或命令只是特定 .Controller

通常有一个和多个子项,每个网页一个(一般来说)。abstract ControllerController

有人可能会说,如果 URL 中存在任何参数,则还会将参数传递给所需的方法。RouterController

注意:遵循单一责任原则的面向对象编程纯粹主义者可能会争辩说,路由 URL 的组件和调度类是两个独立的职责。在这种情况下,类实际上将实例化 并向其方法之一传递从客户端 HTTP 请求派生的任何参数。ControllerDispatcherController

示例 1:控制器,但没有操作或参数。

http://localhost/contact在 GET 请求中,这可能会显示一个表单。

控制器 = 合同

Action = Default(通常为方法)index()

======================

示例 2:控制器和操作,但没有参数。

http://localhost/contact/send在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。

控制器 = 合同

操作 = 发送

======================

示例 3:控制器、操作和参数。

http://localhost/contact/send/sync在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。但是,在这种情况下,可能 JavaScript 未处于活动状态。因此,为了支持正常降级,您可以告诉 使用支持屏幕重绘的 ,并使用 HTTP 标头 而不是 进行响应。 将作为参数传递给 。请注意,我的例子完全是武断和编造的,但我认为它符合要求!ContactControllerViewContent-Type: text/htmlContent-Type: application/jsonsyncContactConroller::send()sync

控制器 = 合同

操作 = 发送

Arguments = // 是的,在数组中传递参数![sync]

类实例化请求的具体子级,从控制器实例调用请求的方法,并向控制器方法传递其参数(如果有)。RouterController

1)你的类应该首先检查是否有可以实例化的具体内容(使用URL中的名称,加上“控制器”一词)。如果找到控制器,请测试是否存在请求的方法(操作)。RouterController

2)如果在运行时无法找到并加载必要的PHP(建议使用自动加载器)来实例化具体的子项,则应检查数组(通常在另一个类名中找到)以查看请求的URL是否与正则表达式匹配其中包含的任何元素。下面是一个类的基本骨架。RouterControllerRouteRoute

注意:= 零个或多个字符,不捕获。.*?

class Route
{
    private $routes = [
        ['url' => 'nieuws/economie/.*?', // regular expression.
         'controller' => 'news',
         'action' => 'economie'],
        ['url' => 'weerbericht/locatie/.*?', // regular expression.
         'controller' => 'weather',
         'action' => 'location']
    ];

    public function __contstruct()
    {

    }

    public function getRoutes()
    {
        return $this->routes;
    }
}

为什么要使用正则表达式?在 URL 中的第二个正斜杠之后,不太可能为数据完成可靠的匹配。

/controller/method/param1/param2/...,其中 param[x] 可以是任何东西

警告:当定位数据包含模式分隔符(在本例中为正斜杠“/”)时,最好更改默认正则表达式模式分隔符 ('/')。几乎任何无效的 URL 字符都是一个不错的选择。

该类的方法将遍历数组,以查看目标 URL 与与二级索引关联的之间是否存在正则表达式匹配。如果找到匹配项,则 then 知道要实例化哪个具体以及要调用的后续方法。必要时,参数将传递给该方法。RouterRoute::routesstringurlRouterController

始终警惕边缘情况,例如表示以下内容的 URL。

`/`   // Should take you to the home page / HomeController by default
`''`  // Should take you to the home page / HomeController by default
`/gibberish&^&*^&*%#&(*$%&*#`   // Reject

评论

0赞 Madara's Ghost 9/15/2012
如果你的版本是一个简化的例子,我所看到的方法(即我的例子)与你的方法相比有什么优势?另外,如果没有找到匹配项怎么办?
0赞 w00 9/15/2012
@MadaraUchiha 我的和你的区别在于,你可以指定符号应该是什么样子。我看到您可以为操作方法添加某些条件。还有其他一些事情。换句话说,您的示例更具可配置性。---我不确定你从哪里得到代码(Symphony?),所以我真的不知道所有选项的真正含义。但总体思路与我的例子完全相同。此外,如果未找到匹配项,则应显示 404 页面 (404Controller)。因为请求的页面根本不存在。
0赞 Madara's Ghost 9/15/2012
那么,它不应该默认为“resource/action/params”吗?
0赞 w00 9/15/2012
@MadaraUchiha 通常,在其他框架中,该选项是当 URI 不包含任何数据时应调用的控制器/操作。否则,当 URI 包含数据,但在 中找不到时,您实际上应该抛出 404 错误。我真的不推荐任何其他方式。defaultRouteBuilder
0赞 Assimilater 9/23/2013
要么是不准确的,要么是我对某些事情感到困惑:在没有 mod 重写的干净 apache 服务器的测试用例中,像 localhost/news 这样的请求不会重新路由到 localhost/index.php?route=news,但会导致 404 错误?
2赞 JvdBerg 9/15/2012 #2

router 类,来自我的框架。代码讲述了这个故事:

class Router
{
  const default_action = 'index';
  const default_controller = 'index';

  protected $request = array();

  public function __construct( $url )
  {
    $this->SetRoute( $url ? $url : self::default_controller );
  }

  /*
  *  The magic gets transforms $router->action into $router->GetAction();
  */
  public function __get( $name )
  {
    if( method_exists( $this, 'Get' . $name ))
      return $this->{'Get' . $name}();
    else
      return null;
  }

  public function SetRoute( $route )
  {
    $route = rtrim( $route, '/' );
    $this->request = explode( '/', $route );
  }

  private function GetAction()
  {
    if( isset( $this->request[1] ))
      return $this->request[1];
    else
      return self::default_action;
  }

  private function GetParams()
  {
    if( count( $this->request ) > 2 )
      return array_slice ( $this->request, 2 );
    else
      return array();
  }

  private function GetPost()
  {
    return $_SERVER['REQUEST_METHOD'] == 'POST';
  }

  private function GetController()
  {
    if( isset( $this->request[0] ))
      return $this->request[0];
    else
      return self::default_controller;
  }

  private function GetRequest()
  {
    return $this->request;
  }

评论

2赞 Madara's Ghost 9/15/2012
我不太习惯使用你的代码。它包含了很多我并不真正想要的全局空间。另外,您是否考虑过使用评论?该代码不是不言自明的。不过我还是会看看。
1赞 Gordon 9/15/2012
@JvdBerg stackoverflow.com/questions/5166087/php-global-in-functions/....此外,您的路由器不应该对SAPI一无所知。从外部传入 URL。
0赞 JvdBerg 9/15/2012
谢谢戈登。我更改了路由器类,SAPI 和全局变量被移动到引导程序中。添加了一些注释
0赞 Anthony Rutledge 9/8/2018
错误检查不足。几乎没有,如果有的话。做出太多假设。
1赞 Vincent 5/2/2019
一行评论仍然不足以解释新手正在发生的事情。