URL路由器和调度器有什么区别?

35
我想了解 URL 路由和 Dispatcher 的区别,因为在互联网上搜索到了一些有趣的东西,只是不知道它们是否相似或者它们的功能是否相反。有人能告诉我它们各自是什么,以及提供一个例子吗?
我不知道 URL 路由器和调度程序的区别,在互联网上搜索到的内容有时候会让人觉得调度程序就是路由器,而路由器似乎就是调度程序,最终不知道它们各自的正确性、它们各自是什么,以及如何实现它们。
谢谢。

你指的是哪个框架? - uzyn
3
实际上,我提出的问题是一般性的,即并非针对某个具体的框架,而是仅限于URL路由器和调度程序的功能。 - YakumaruBR
2个回答

95

框架和库对路由器和分发器的职责解释会有所不同。下面是我对这两个服务职责的理解。并不是说这是唯一的解释方式,也不是其他解释方式是错误的。

概念

路由

这有点像在加油站或便利店问路。你穿过城镇,需要弄清如何到达最近的酒店。你进去问工作人员,他们会指向正确的方向,或者至少你希望这些方向是正确的。路由就是这样。当资源请求到来时,路由器提供必要的方向,以使请求到达正确的资源。

在大多数主要的框架中,您将把特定的请求URL路由到一个对象和方法上,以完成请求。通常情况下,您还将看到路由器从URL中解析出动态参数。例如,如果您通过/users/1234访问用户,其中1234是用户ID,则路由器将解析出ID部分,并将其作为到资源的方向的一部分提供。

分发

分发使用路由步骤中的信息实际生成资源。如果路由步骤是询问路线,则分发是按照这些路线实际进行的过程。分发知道要创建什么以及生成资源所需的步骤,但只有在从路由器获取方向后才知道。

实现

这些示例实现故意非常简单和天真。您不应在任何生产环境中使用此内容,除非进行了重大改进。

在此示例中,我们不是将路由到对象和方法,而是将其路由到可调用函数。这也证明了您不需要路由到对象;只要分发器能够正确地获取正确的资源,您就可以路由到任何数据。

首先,我们需要一些内容来路由。让我们创建一个简单的请求对象以进行匹配。

<?php

class Request {

    private $method;
    private $path;

    function __construct($method, $path) {
        $this->method = $method;
        $this->path = $path;
    }

    function getMethod() {
        return $this->method;
    }

    function getPath() {
        return $this->path;
    }

}

现在我们可以进行匹配,让我们来看一个简单的路由器实现。

<?php

class Router {

    private $routes = [
        'get' => [],
        'post' => []
    ];

    function get($pattern, callable $handler) {
        $this->routes['get'][$pattern] = $handler;
        return $this;
    }

    function post($pattern, callable $handler) {
        $this->routes['post'][$pattern] = $handler;
        return $this;
    }

    function match(Request $request) {
        $method = strtolower($request->getMethod());
        if (!isset($this->routes[$method])) {
            return false;
        }

        $path = $request->getPath();
        foreach ($this->routes[$method] as $pattern => $handler) {
            if ($pattern === $path) {
                return $handler;
            }
        }

        return false;
    }

}

现在我们需要一种方法来调用配置好的$handler来处理给定的请求。

<?php

class Dispatcher {

    private $router;

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

    function handle(Request $request) {
        $handler = $this->router->match($request);
        if (!$handler) {
            echo "Could not find your resource!\n";
            return;
        }

        $handler();
    }

}

现在,让我们把所有的东西都放在一起,展示如何使用这些简单实现。

<?php

$router = new Router();
$router->get('foo', function() { echo "GET foo\n"; });
$router->post('bar', function() { echo "POST bar\n"; });

$dispatcher = new Dispatcher($router);

$dispatcher->handle(new Request('GET', 'foo'));
$dispatcher->handle(new Request('POST', 'bar'));
$dispatcher->handle(new Request('GET', 'qux'));

你可以通过查看http://3v4l.org/gbsoJ来了解这种实现的示例。

总结

这个例子的实现旨在传达路由和调度的概念。而实际上,执行这些操作要比我的示例复杂得多。通常,路由器会使用正则表达式去匹配请求,并在匹配时查看其他请求属性。此外,你会看到一些库利用解析器与路由器交互,以便你可以传递更多的可调用函数,解析器将确保与$handler匹配的内容能够转换为可调用函数。

另外,还有很多你应该使用的示例和实现。对于我的个人项目,我喜欢FastRoute的易用性和性能。但是,几乎所有主流框架都有自己的实现。你也应该去看看那些。


讲解得非常清楚!! - kabirbaidhya
感谢您澄清请求、路由器和调度器类的角色。有时很难看到单一职责原则的作用,但在这里非常清晰(例如)。太好了。我一直想知道PHP-FIG PSR-7如何适应,它似乎是您示例中的“请求”级别。 - Anthony Rutledge
我喜欢这个例子。你可以在Router类的结尾处用if (isset($this->routes[$method][$path])) {return $this->routes[$method][$path]}替换foreach循环。非常感谢。 - Anthony Rutledge
1
@Daniele 你可以让你的调度程序更加健壮,能够执行不仅仅是匿名函数。想象一下一个 CallableResolver 接口,Dispatcher 可以将其作为构造函数依赖项进行接受。CallableResolver 在其上有一个 resolve($routeData) : ?callable 方法,Dispatcher 将解析后的路由数据传递给它; 它将始终返回可调用对象或 null。一个 CallableResolverChain 可以循环遍历一系列 CallableResolver 直到找到一个匹配项。 - Charles Sprayberry
1
我原本想避免加一的评论,但这绝对值得。告我也没关系。 - Stark
显示剩余5条评论

0

路由是对资源的引用,而调度是获取该资源的操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接