路由

Slim Framework 的路由器构建在Fast Route组件之上,它非常快速和稳定。当我们使用这个组件来完成我们所有的路由时,应用程序的核心已经完全与它分离,并且接口已经到位,为使用其他路由库铺平了道路。

如何创建路线

您可以使用Slim\App实例上的代理方法定义应用程序路由。Slim 框架为最流行的 HTTP 方法提供了方法。

获取路线

GET您可以使用 Slim 应用程序的方法添加一个仅处理 HTTP 请求的路由get()它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->get('/books/{id}', function ($request, $response, array $args) {
    // Show book identified by $args['id']
});

邮寄路线

POST您可以使用 Slim 应用程序的方法添加一个仅处理 HTTP 请求的路由post()它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->post('/books', function ($request, $response, array $args) {
    // Create new book
});

PUT路线

PUT您可以使用 Slim 应用程序的方法添加一个仅处理 HTTP 请求的路由put()它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->put('/books/{id}', function ($request, $response, array $args) {
    // Update book identified by $args['id']
});

删除路线

DELETE您可以使用 Slim 应用程序的方法添加一个仅处理 HTTP 请求的路由delete()它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->delete('/books/{id}', function ($request, $response, array $args) {
    // Delete book identified by $args['id']
});

选项路线

OPTIONS您可以使用 Slim 应用程序的方法添加一个仅处理 HTTP 请求的路由options()它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->options('/books/{id}', function ($request, $response, array $args) {
    // Return response headers
});

补丁路线

PATCH您可以使用 Slim 应用程序的方法添加一个仅处理 HTTP 请求的路由patch()它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->patch('/books/{id}', function ($request, $response, array $args) {
    // Apply changes to book identified by $args['id']
});

任何路线

您可以使用 Slim 应用程序的any()方法添加一个处理所有 HTTP 请求方法的路由。它接受两个参数:

  1. 路由模式(带有可选的命名占位符)
  2. 路由回调
$app->any('/books/[{id}]', function ($request, $response, array $args) {
    // Apply changes to books or book identified by $args['id'] if specified.
    // To check which method is used: $request->getMethod();
});

请注意,第二个参数是回调。您可以指定一个实现该__invoke()方法的类而不是闭包。然后您可以在其他地方进行映射:

$app->any('/user', 'MyRestfulController');

自定义路线

您可以使用 Slim 应用程序的map()方法添加一个处理多个 HTTP 请求方法的路由。它接受三个参数:

  1. HTTP 方法数组
  2. 路由模式(带有可选的命名占位符)
  3. 路由回调
$app->map(['GET', 'POST'], '/books', function ($request, $response, array $args) {
    // Create new book or list all books
});

路由回调

上述每个路由方法都接受一个回调例程作为其最终参数。这个参数可以是任何 PHP 可调用的,默认情况下它接受三个参数。

  • Request第一个参数是Psr\Http\Message\ServerRequestInterface表示当前 HTTP 请求的对象。
  • Response第二个参数是Psr\Http\Message\ResponseInterface表示当前 HTTP 响应的对象。
  • Arguments第三个参数是一个关联数组,其中包含当前路由的命名占位符的值。

将内容写入响应

您可以通过两种方式将内容写入 HTTP 响应。首先,您可以简单地echo()从路由回调中获取内容。此内容将附加到当前 HTTP 响应对象。其次,您可以返回一个Psr\Http\Message\ResponseInterface对象。

闭合装订

如果您使用依赖容器Closure实例作为路由回调,则闭包的状态将绑定到实例Container这意味着您将可以通过关键字访问闭包$this的 DI 容器实例:

$app->get('/hello/{name}', function ($request, $response, array $args) {
    // Use app HTTP cookie service
    $this->get('cookies')->set('name', [
        'value' => $args['name'],
        'expires' => '7 days'
    ]);
});
小心!

Slim 不支持static闭包。

重定向助手

GET您可以使用 Slim 应用程序的方法添加一个将 HTTP 请求重定向到不同 URL 的路由redirect()它接受三个参数:

  1. 要重定向的路由模式(带有可选的命名占位符)from
  2. 重定向的位置to,可以是一个string或一个 Psr\Http\Message\UriInterface
  3. 要使用的 HTTP 状态代码(可选;302如果未设置)
$app->redirect('/books', '/library', 301);

redirect()路由响应请求的状态代码和Location 设置为第二个参数的标头。

路由策略

路由回调签名由路由策略决定。默认情况下,Slim 期望路由回调接受请求、响应和路由占位符参数数组。这称为 RequestResponse 策略。但是,您可以通过简单地使用不同的策略来更改预期的路由回调签名。例如,Slim 提供了一种替代策略,称为RequestResponseArgs接受请求和响应,加上每个路由占位符作为单独的参数。

以下是使用此替代策略的示例:

<?php
use Slim\Factory\AppFactory;
use Slim\Handlers\Strategies\RequestResponseArgs;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

/**
 * Changing the default invocation strategy on the RouteCollector component
 * will change it for every route being defined after this change being applied
 */
$routeCollector = $app->getRouteCollector();
$routeCollector->setDefaultInvocationStrategy(new RequestResponseArgs());

$app->get('/hello/{name}', function ($request, $response, $name) {
    $response->getBody()->write($name);
    
    return $response;
});

或者,您可以在每个路由的基础上设置不同的调用策略:

<?php
use Slim\Factory\AppFactory;
use Slim\Handlers\Strategies\RequestResponseArgs;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();
$routeCollector = $app->getRouteCollector();

$route = $app->get('/hello/{name}', function ($request, $response, $name) {
    $response->getBody()->write($name);
    
    return $response;
});
$route->setInvocationStrategy(new RequestResponseArgs());

您可以通过实施Slim\Interfaces\InvocationStrategyInterface.

路线占位符

上述每种路由方法都接受与当前 HTTP 请求 URI 匹配的 URL 模式。路由模式可以使用命名占位符来动态匹配 HTTP 请求 URI 段。

格式

路由模式占位符以 开头{,后跟占位符名称,以 结尾}这是一个名为的示例占位符name

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
// ...

$app->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, array $args) {
    $name = $args['name'];
    $response->getBody()->write("Hello, $name");
    
    return $response;
});

可选段

要使部分成为可选的,只需用方括号括起来:

$app->get('/users[/{id}]', function ($request, $response, array $args) {
    // responds to both `/users` and `/users/123`
    // but not to `/users/`

    return $response;
});

嵌套支持多个可选参数:

$app->get('/news[/{year}[/{month}]]', function ($request, $response, array $args) {
    // responds to `/news`, `/news/2016` and `/news/2016/03`
    // ...

    return $response;
});

对于“Unlimited”可选参数,您可以这样做:

$app->get('/news[/{params:.*}]', function ($request, $response, array $args) {
    // $params is an array of all the optional segments
    $params = explode('/', $args['params']);
    // ...

    return $response;
});

在此示例中,URI/news/2016/03/20将导致$params包含三个元素的数组:['2016', '03', '20']

正则表达式匹配

默认情况下,占位符写在里面{}并且可以接受任何值。但是,占位符也可以要求 HTTP 请求 URI 匹配特定的正则表达式。如果当前 HTTP 请求 URI 与占位符正则表达式不匹配,则不会调用该路由。id这是一个需要一位或多位数字的占位符示例。

$app->get('/users/{id:[0-9]+}', function ($request, $response, array $args) {
    // Find user identified by $args['id']
    // ...

    return $response;
});

路线名称

应用程序路径可以指定一个名称。如果您想使用 RouteParser 的方法以编程方式生成指向特定路由的 URL,这将很有用urlFor()上面介绍的每一个路由方法都会返回一个Slim\Route对象,而这个对象暴露了一个setName()方法。

$app->get('/hello/{name}', function ($request, $response, array $args) {
    $response->getBody()->write("Hello, " . $args['name']);
    return $response;
})->setName('hello');

您可以使用应用程序 RouteParser 的方法为此命名路由生成 URL urlFor()

$routeParser = $app->getRouteCollector()->getRouteParser();
echo $routeParser->urlFor('hello', ['name' => 'Josh'], ['example' => 'name']);

// Outputs "/hello/Josh?example=name"

RouteParser 的urlFor()方法接受三个参数:

  • $routeName路线名称。路由的名称可以通过$route->setName('name'). 路由映射方法返回一个实例,Route因此您可以在映射路由后直接设置名称。例如:$app->get('/', function () {...})->setName('name')
  • $data路由模式占位符和替换值的关联数组。
  • $queryParams要附加到生成的 url 的查询参数的关联数组。

路线组

为了帮助将路由组织成逻辑组,Slim\App还提供了一种group()方法。每个组的路由模式都添加到其中包含的路由或组之前,并且组模式中的任何占位符参数最终都可用于嵌套路由:

use Slim\Routing\RouteCollectorProxy;
// ...

$app->group('/users/{id:[0-9]+}', function (RouteCollectorProxy $group) {
    $group->map(['GET', 'DELETE', 'PATCH', 'PUT'], '', function ($request, $response, array $args) {
        // Find, delete, patch or replace user identified by $args['id']
        // ...

        return $response;
    })->setName('user');
    
    $group->get('/reset-password', function ($request, $response, array $args) {
        // Route for /users/{id:[0-9]+}/reset-password
        // Reset the password for user identified by $args['id']
        // ...

        return $response;
    })->setName('user-password-reset');
});

组模式可以为空,启用不共享公共模式的路由的逻辑分组。

use Slim\Routing\RouteCollectorProxy;
// ...

$app->group('', function (RouteCollectorProxy $group) {
    $group->get('/billing', function ($request, $response, array $args) {
        // Route for /billing
        return $response;
    });
    
    $group->get('/invoice/{id:[0-9]+}', function ($request, $response, array $args) {
        // Route for /invoice/{id:[0-9]+}
        return $response;
    });
})->add(new GroupMiddleware());

注意在组闭包内部,Slim 将闭包绑定到容器实例。

  • 在路由闭包中,$this绑定到的实例Psr\Container\ContainerInterface

路由中间件

您还可以将中间件附加到任何路由或路由组。

use Slim\Routing\RouteCollectorProxy;
// ...

$app->group('/foo', function (RouteCollectorProxy $group) {
    $group->get('/bar', function ($request, $response, array $args) {
        // ...
        return $response;
    })->add(new RouteMiddleware());
})->add(new GroupMiddleware());

路由表达式缓存

可以通过启用路由器缓存RouteCollector::setCacheFile()请参阅以下示例:

<?php
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

/**
 * To generate the route cache data, you need to set the file to one that does not exist in a writable directory.
 * After the file is generated on first run, only read permissions for the file are required.
 *
 * You may need to generate this file in a development environment and committing it to your project before deploying
 * if you don't have write permissions for the directory where the cache file resides on the server it is being deployed to
 */
$routeCollector = $app->getRouteCollector();
$routeCollector->setCacheFile('/path/to/cache.file');

容器分辨率

您不仅限于为路线定义功能。在 Slim 中有几种不同的方式来定义你的路由动作函数。

除了一个函数,你还可以使用:

  • 容器键:方法
  • 类别:方法
  • 类实现__invoke()方法
  • 容器密钥

此功能由 Slim 的 Callable Resolver Class 启用。它将字符串条目转换为函数调用。例子:

$app->get('/', '\HomeController:home');

或者,您可以利用 PHP 的::class运算符,它可以很好地与 IDE 查找系统配合使用并产生相同的结果:

$app->get('/', \HomeController::class . ':home');

您还可以传递一个数组,其中第一个元素将包含类的名称,第二个将包含被调用方法的名称:

$app->get('/', [\HomeController::class, 'home']);

在上面的这段代码中,我们定义了一个路由并告诉 Slim在类上/执行该方法home()HomeController

Slim 首先在容器中查找 的条目HomeController,如果找到它将使用该实例,否则它将以容器作为第一个参数调用它的构造函数。创建类的实例后,它将使用您定义的任何策略调用指定的方法。

向容器注册控制器

使用 action 方法创建控制器home构造函数应该接受所需的依赖项。例如:

<?php

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Twig;

class HomeController
{
    private $view;

    public function __construct(Twig $view)
    {
        $this->view = $view;
    }
    
    public function home(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
    {
      // your code here
      // use $this->view to render the HTML
      // ...

      return $response;
    }
}

在容器中创建一个工厂,用依赖项实例化控制器:

use Psr\Container\ContainerInterface;
// ...

$container = $app->getContainer();

$container->set('HomeController', function (ContainerInterface $container) {
    // retrieve the 'view' from the container
    $view = $container->get('view');
    
    return new HomeController($view);
});

这允许您利用容器进行依赖项注入,因此您可以将特定的依赖项注入控制器。

允许 Slim 实例化控制器

或者,如果该类在容器中没有条目,则 Slim 会将容器的实例传递给构造函数。您可以构造具有许多操作的控制器,而不是只处理一个操作的可调用类。

<?php

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class HomeController
{
   private $container;

   // constructor receives container instance
   public function __construct(ContainerInterface $container)
   {
       $this->container = $container;
   }

   public function home(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
   {
        // your code to access items in the container... $this->container->get('');

        return $response;
   }

   public function contact(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
   {
        // your code to access items in the container... $this->container->get('');

        return $response;
   }
}

您可以像这样使用您的控制器方法。

$app->get('/', \HomeController::class . ':home');
$app->get('/contact', \HomeController::class . ':contact');

使用可调用类

您不必在可调用路由中指定方法,只需将其设置为可调用类即可,例如:

<?php

use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class HomeAction
{
   private $container;

   public function __construct(ContainerInterface $container)
   {
       $this->container = $container;
   }

   public function __invoke(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
   {
        // your code to access items in the container... $this->container->get('');

        return $response;
   }
}

你可以像这样使用这个类。

$app->get('/', \HomeAction::class);

同样,与控制器一样,如果您将类名注册到容器中,那么您可以创建一个工厂并将您需要的特定依赖项注入到您的操作类中。