MVC 路由是如何工作的?
所以我开始更深入地研究 MVC(真正的 MVC,而不是框架 MVC),并且我正在尝试开发一个小型框架.我通过阅读其他框架(例如 Symphony 和 Zend)来工作,了解它们的工作方式,并尝试自己实现.
So I've started studying MVC (real MVC, not framework MVC) a bit more in-depth, and I'm attempting to develop a small framework. I'm working by reading other frameworks such as Symphony and Zend, seeing how they do their job, and attempt to implement it myself.
我卡住的地方是 URL 路由系统:
The place where I got stuck was the URL routing system:
<?php
namespace ApplicationCommon;
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>[^/.,;?
]+)", $notation);
//@TODO: Continue pattern replacement!!
}
}
/* How a single entry looks like
* "main": {
"notation": "/:action",
"defaults": {
"resource" : "Authentication",
},
"conditions": {
":action" : "(login)|(register)"
}
},
*/
我就是无法将头正确地缠绕在它上面.这里的应用工作流程是什么?
I just can't get my head wrapped around it properly. What is the application workflow from here?
pattern 生成了,大概是一个Route
对象保存在Request
对象下什么的,然后呢?它是如何工作的?
The pattern is generated, probably a Route
object to be kept under the Request
object or something, then what? How does it work?
P.S. 在这里寻找一个真实的、解释清楚的答案.我真的很想了解这个主题.如果有人花时间写一个真正详尽的答案,我将不胜感激.
P.S. Looking for a real, well explained answer here. I really want to understand the subject. I would appreciate if someone took the time to write a real elaborate answer.
推荐答案
一个 MVC Router
类(它是更广泛的前端控制器的一部分)分解 HTTP 请求的URL——特别是路径组件(可能还有查询字符串).
An MVC Router
class (which is part of a broader Front Controller) breaks down an HTTP request's URL--specifically, the path component (and potentially the query string).
Router
尝试将路径组件的前一个或两个部分与相应的路由组合(Controller
/Action [method],或者只是一个执行默认操作(方法)的Controller
.
The Router
attempts to match the first one, or two, parts of the path component to a corresponding route combination (Controller
/ Action [method], or just a Controller
that executes a default action (method).
一个动作或命令,只是一个特定Controller
的方法.
An action, or command, is simply a method off of a specific Controller
.
通常有一个抽象控制器
和许多Controller
的子节点,每个网页一个(一般来说).
There is usually an abstract Controller
and many children of Controller
, one for each webpage (generally speaking).
有些人可能会说 Router
还会将参数传递给所需的 Controller
方法,如果 URL 中存在任何参数.
Some might say that the Router
also passes arguments to the desired Controller
's method, if any are present in the URL.
注意:遵循单一职责原则的面向对象编程纯粹主义者可能会争辩说,路由 URL 的组件和调度code>Controller 类是两个独立的职责.在这种情况下,Dispatcher
类实际上会实例化 Controller
并将其方法之一传递给从客户端 HTTP 请求派生的任何参数.
Note: Object-oriented programming purists, following the Single Responsibility Principle, might argue that routing components of a URL and dispatching a Controller
class are two separate responsibilities. In that case, a Dispatcher
class would actually instantiate the Controller
and pass one of its methods any arguments derived from the client HTTP request.
示例 1:Controller
,但没有操作或参数.
Example 1: Controller
, but no action or arguments.
http://localhost/contact
在 GET 请求中,这可能会显示一个表单.
http://localhost/contact
On a GET request, this might display a form.
控制器 = 合约
Action = 默认(通常是 index()
方法)
Action = Default (commonly an index()
method)
======================
======================
示例 2:Controller
和动作,但没有参数.
Example 2: Controller
and action, but no arguments.
http://localhost/contact/send
在 POST 请求中,这可能会启动服务器端验证并尝试发送消息.
http://localhost/contact/send
On a POST request, this might kick of server-side validation and attempt to send a message.
控制器 = 合约
操作 = 发送
======================
======================
示例 3:Controller
、动作和参数.
Example 3: Controller
, action, and arguments.
http://localhost/contact/send/sync
在 POST 请求中,这可能会启动服务器端验证并尝试发送消息.但是,在这种情况下,JavaScript 可能未处于活动状态.因此,为了支持优雅降级,您可以告诉 ContactController
使用支持屏幕重绘的 View
并使用 Content-Type: text/的 HTTP 标头响应html
,而不是 Content-Type: application/json
.sync
将作为参数传递给 ContactConroller::send()
.请注意,我的 sync
示例完全是随意编造的,但我认为它符合要求!
http://localhost/contact/send/sync
On a POST request, this might kick of server-side validation and attempt to send a message. However, in this case, maybe JavaScript is not active. Thus, to support graceful degradation, you can tell the ContactController
to use a View
that supports screen redraw and responds with an HTTP header of Content-Type: text/html
, instead of Content-Type: application/json
. sync
would be passed as an argument to ContactConroller::send()
. Note, my sync
example was totally arbitrary and made up, but I thought it fit the bill!
控制器 = 合约
操作 = 发送
Arguments = [sync]
//是的,在数组中传递参数!
Arguments = [sync]
// Yes, pass arguments in an array!
Router
类实例化请求的具体子Controller
,从控制器实例调用请求的方法,并将其参数(如果有)传递给控制器方法.
A Router
class instantiates the requested, concrete child Controller
, calls the requested method from the controller instance, and passes the controller method its arguments (if any).
1) 您的 Router
类应该首先检查是否有一个具体的 Controller
可以实例化(使用找到的名称在 URL 中,加上控制器"一词).如果找到控制器,测试请求的方法是否存在(行动).
1) Your Router
class should first check to see if there is a concrete Controller
that it can instantiate (using the name as found in the URL, plus the word "Controller"). If the controller is found, test for the presence of the requested method (action).
2) 如果 Router
在运行时找不到并加载必要的 PHP(使用 autoloader 被建议)实例化一个具体的 Controller
子项,然后它应该检查一个数组(通常在另一个类名 Route
) 以查看请求的 URL 使用正则表达式匹配 中包含的任何元素.Route
类的基本框架如下.
2) If the Router
cannot find and load the necessary PHP at runtime (using an autoloader is advised) to instantiate a concrete Controller
child, it should then check an array (typically found in another class name Route
) to see if the requested URL matches, using regular expressions, any of the elements contained within. A basic skeleton of a Route
class follows.
注意:.*?
= 零个或多个任意字符,非捕获.
Note: .*?
= Zero, or more, of any character, non-capturing.
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 中的第二个正斜杠之后完成数据的可靠匹配.
Why use a regular expression? One is not likely to get reliable matching accomplished for data after the second forward slash in the URL.
/controller/method/param1/param2/...
,其中 param[x] 可以是任何东西!
/controller/method/param1/param2/...
, where param[x] could be anything!
警告:当目标数据包含模式分隔符(在本例中为正斜杠/")时,最好更改默认的正则表达式模式分隔符 ('/').几乎所有非有效的 URL 字符将是一个不错的选择.
Warning: It is good practice change the default regular expression pattern delimiter ('/') when targeting data contains the pattern delimiter (in this case, forward slashes '/'. Almost any non-valid URL character would be a great choice.
Router
类的一个方法将遍历 Route::routes
数组以查看目标 URL 和 之间是否存在正则表达式匹配string
value 与第二级 url
索引相关联.如果找到匹配项,Router
然后知道要实例化哪个具体的 Controller
以及要调用的后续方法.参数将根据需要传递给方法.
A method of the Router
class will iterate over the Route::routes
array to see if there is a regular expression match between the target URL and the string
value associated with a 2nd level url
index. If a match is found, the Router
then knows which concrete Controller
to instantiate and the subsequent method to call. Arguments will be passed to the method as necessary.
始终警惕边缘情况,例如表示以下内容的 URL.
Always be wary of edge cases, such as URLs representing the following.
`/` // Should take you to the home page / HomeController by default
`''` // Should take you to the home page / HomeController by default
`/gibberish&^&*^&*%#&(*$%&*#` // Reject
相关文章