提问人:Madara's Ghost 提问时间:9/15/2012 最后编辑:PeeHaaMadara's Ghost 更新时间:5/7/2019 访问量:34808
MVC 路由如何工作?
How does MVC routing work?
问:
因此,我开始更深入地研究 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)"
}
},
*/
我只是无法正确地将我的头包裹在它身上。从这里开始的应用程序工作流程是什么?
模式生成了,可能是要放在对象下面的对象什么的,然后呢?它是如何工作的?Route
Request
附言在这里寻找一个真实的、解释清楚的答案。我真的很想了解这个主题。如果有人花时间写一个真正详细的答案,我将不胜感激。
答:
MVC 类(它是更广泛的 Front Controller 的一部分)分解 HTTP 请求的 URL,特别是路径组件(可能还有查询字符串)。Router
尝试将路径组件的前一个或两个部分与相应的路由组合 ( / Action [method] 匹配,或者只是执行默认操作 (method) 的路由组合。Router
Controller
Controller
操作或命令只是特定 .Controller
通常有一个和多个子项,每个网页一个(一般来说)。abstract Controller
Controller
有人可能会说,如果 URL 中存在任何参数,则还会将参数传递给所需的方法。Router
Controller
注意:遵循单一责任原则的面向对象编程纯粹主义者可能会争辩说,路由 URL 的组件和调度类是两个独立的职责。在这种情况下,类实际上将实例化 并向其方法之一传递从客户端 HTTP 请求派生的任何参数。Controller
Dispatcher
Controller
示例 1:控制器
,但没有操作或参数。
http://localhost/contact
在 GET 请求中,这可能会显示一个表单。
控制器 = 合同
Action = Default(通常为方法)index()
======================
示例 2:控制器
和操作,但没有参数。
http://localhost/contact/send
在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。
控制器 = 合同
操作 = 发送
======================
示例 3:控制器
、操作和参数。
http://localhost/contact/send/sync
在 POST 请求中,这可能会启动服务器端验证并尝试发送消息。但是,在这种情况下,可能 JavaScript 未处于活动状态。因此,为了支持正常降级,您可以告诉 使用支持屏幕重绘的 ,并使用 HTTP 标头 而不是 进行响应。 将作为参数传递给 。请注意,我的例子完全是武断和编造的,但我认为它符合要求!ContactController
View
Content-Type: text/html
Content-Type: application/json
sync
ContactConroller::send()
sync
控制器 = 合同
操作 = 发送
Arguments = // 是的,在数组中传递参数![sync]
类实例化请求的具体子级,从控制器实例调用请求的方法,并向控制器方法传递其参数(如果有)。Router
Controller
1)你的类应该首先检查是否有可以实例化的具体内容(使用URL中的名称,加上“控制器”一词)。如果找到控制器,请测试是否存在请求的方法(操作)。Router
Controller
2)如果在运行时无法找到并加载必要的PHP(建议使用自动加载器)来实例化具体的子项,则应检查数组(通常在另一个类名中找到)以查看请求的URL是否与正则表达式匹配其中包含的任何元素。下面是一个类的基本骨架。Router
Controller
Route
Route
注意:= 零个或多个字符,不捕获。.*?
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 知道要实例化哪个具体以及要调用的后续方法。必要时,参数将传递给该方法。Router
Route::routes
string
url
Router
Controller
始终警惕边缘情况,例如表示以下内容的 URL。
`/` // Should take you to the home page / HomeController by default
`''` // Should take you to the home page / HomeController by default
`/gibberish&^&*^&*%#&(*$%&*#` // Reject
评论
default
RouteBuilder
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;
}
评论