Flash Messages
最后更新于:2022-04-01 22:34:24
# Flash Messages
Coming soon.
';
CSRF 保护
最后更新于:2022-04-01 22:34:22
# CSRF 保护
[Edit This Page](https://github.com/slimphp/Slim-Website/tree/gh-pages/docs/features/csrf.md)
Slim 3 使用独立可选的 PHP 组件 [slimphp/Slim-Csrf](https://github.com/slimphp/Slim-Csrf) 来保护应用程序免遭 CSRF(跨站请求伪造)。本组件为每个请求生成一个唯一 token ,验证来源于客户端 HTML 表单产生的 POST 请求。
## 安装
在你的项目的根目录下执行这个bash命令:
```
composer require slim/csrf
```
## 用法
这个 `slimphp/Slim-Csrf` 组件包含一个应用程序中间件。像这样将它添加到你的应用程序中:
```
// Add middleware to the application
$app = new \Slim\App;
$app->add(new \Slim\Csrf\Guard);
// Create your application routes...
// Run application
$app->run();
```
## 提取 CSRF token 的名称和值
最新的 CSRF token 作为 PSR7 请求对象的属性,其名称和值是可获取的。每个请求的CSRF token 都是唯一的。你可以像这样提取当前的 CSRF token 的名称和值:
```
$app->get('/foo', function ($req, $res, $args) {
// Fetch CSRF token name and value
$name = $req->getAttribute('csrf_name');
$value = $req->getAttribute('csrf_value');
// TODO: Render template with HTML form and CSRF token hidden field
});
```
你应该将 CSRF token 的名称和值船体给模板,这样它们就能和 HTML 表单 POST 请求一起被提交。它们经常被存储为 HTML 表单的一个隐藏字段。
';
HTTP 缓存
最后更新于:2022-04-01 22:34:19
# HTTP 缓存
[Edit This Page](https://github.com/slimphp/Slim-Website/tree/gh-pages/docs/features/caching.md)
Slim 3 使用 [slimphp/Slim-HttpCache](https://github.com/slimphp/Slim-HttpCache) 这款独立的 PHP 组件作为可选的 HTTP 缓存工具。可以使用这个组件创建并返回包含 `Cache`, `Expires`, `ETag`, 和 `Last-Modified` 头的 HTTP 响应,以控制何时以及如何使用客户端缓存保留应用程序的输出。
## 安装
在你的项目的根目录下执行这个bash命令:
```
composer require slim/http-cache
```
## 用法
这个 `slimphp/Slim-HttpCache` 组件包含一个服务提供程序和一个应用程序中间件。你必须在你的应用程序中添加这两个玩意,像这样:
```
// Register service provider with the container
$container = new \Slim\Container;
$container['cache'] = function () {
return new \Slim\HttpCache\CacheProvider();
};
// Add middleware to the application
$app = new \Slim\App($container);
$app->add(new \Slim\HttpCache\Cache('public', 86400));
// Create your application routes...
// Run application
$app->run();
```
## ETag
使用服务提供程序的 `withEtag()` 方法创建一个带有指定 `ETag` 头的响应对象。这个方法接收一个 PSR7 响应对象,而且它返回一个带有新 HTTP 头的 PSR7 响应的拷贝。
```
$app->get('/foo', function ($req, $res, $args) {
$resWithEtag = $this->cache->withEtag($res, 'abc');
return $resWithEtag;
});
```
## Expires
使用服务提供程序的 `withExpires()` 方法创建一个带有指定 `Expires` 头的响应对象。这个方法接收一个 PSR7 响应对象,而且它返回一个带有新的 HTTP 头的 PSR7 响应的拷贝。
```
$app->get('/bar',function ($req, $res, $args) {
$resWithExpires = $this->cache->withExpires($res, time() + 3600);
return $resWithExpires;
});
```
## Last-Modified
使用服务提供程序的`withLastModified()` 方法创建一个带有指定 `Last-Modified` 头的响应对象。这个方法接收一个 PSR7 响应对象,而且它返回一个带有新 HTTP 头的 PSR7 响应的拷贝。
```
//Example route with LastModified
$app->get('/foobar',function ($req, $res, $args) {
$resWithLastMod = $this->cache->withLastModified($res, time() - 3600);
return $resWithLastMod;
});
```
';
模板
最后更新于:2022-04-01 22:34:17
# 模板
Slim 没有传统 MVC 框架的视图(view)层。相反,Slim 的“视图”就是 HTTP 响应。Slim 应用程序的每个路由都为准备和返回恰当的 PSER 7 响应对象负责。
> Slim’s “view” is the HTTP response.
话虽如此,但 Slim 项目提供了 [Twig-View](#the-slimtwig-view-component) 和 [PHP-View](#the-slimphp-view-component) 组件帮助你将模版渲染为 PSR7 响应对象。
## The slim/twig-view 组件
[Twig-View](https://github.com/slimphp/Twig-View) PHP 组件帮助你渲染应用程序中的 [Twig](http://twig.sensiolabs.org/) 模版。这个组件可以在 Packageist 上找到。可以像这样使用 composer 轻易地安装:
```
composer require slim/twig-view
```
Figure 1: Install slim/twig-view component.
下一步,你需要在 Slim 应用容器中将此组将注册为服务,像这样:
```
getContainer();
// Register component on container $container['view'] = function ($container) {
$view = new \Slim\Views\Twig('path/to/templates', [
'cache' => 'path/to/cache'
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container['router'],
$container['request']->getUri()
));
return $view;
};
```
Figure 2: Register slim/twig-view component with container.
记住:“cache” 可以设置为 false 禁用它,‘auto_reload’ 选项也是如此,这在开发环境中很有用。了解更多信息,查阅: [Twig environment options](http://twig.sensiolabs.org/api/master/Twig_Environment.html#method___construct)
现在你可以使用应用程序内部的 `slim/twig-view` 组件服务并将其写入到 PSR 7 响应对象中,像这样:
```
// Render Twig template in route
$app->get('/hello/{name}', function ($request, $response, $args) {
return $this->view->render($response, 'profile.html', [
'name' => $args['name']
]);
})->setName('profile');
// Run app
$app->run();
```
Figure 3: Render template with slim/twig-view container service.
在这个例子中,在路由回调中被调用的 `$this->view`,是容器服务返回的 `\Slim\Views\Twig` 实例的一个参考(reference)。`\Slim\Views\Twig` 实例的 `render()` 方法接收一个 PSR7 响应对象作为它的第一个参数,Twig 模版路径作为它的第二个参数,模板变量的数组作为它的最后一个参数。这个 `render()` 方法返回一个新的 PSR7 响应对象,它的响应体是由 Twig 模版渲染的。
### path_for() 方法
`slim/twig-view` 组件为 Twig 模版暴露了一个自定义 `path_for()` 函数。你可以使用这个函数生成完整的指向应用程序中任意已命名路由的 URL。`path_for()` 函数接收两个参数:
1. 路由名称
2. 路由占位符和替换值的散列(hash)
第二个参数的关键字须与已选择的路由的模式占位符一致。这是一个示例的 Twig 模版,它描述了上面的示例 Slim 应用程序中的 “profile” 路由的链接 URL。
```
{% extends "layout.html" %}
{% block body %}
';
User List
{% endblock %} ``` ## slim/php-view 组件 [PHP-View](https://github.com/slimphp/PHP-View) PHP 组件帮助你渲染 PHP 模版。该组件可以在 Packagist 上找到,像这样使用 Composer 安装: ``` composer require slim/php-view ``` Figure 4: Install slim/php-view component. 在 Slim App 的容器中,将此组件注册为服务,这么做: ``` getContainer(); // Register component on container $container['view'] = function ($container) { return new \Slim\Views\PhpRenderer('path/to/templates/with/trailing/slash/'); }; ``` Figure 5: Register slim/php-view component with container. 使用 view 组件渲染 PHP 视图: ``` // Render Twig template in route $app->get('/hello/{name}', function ($request, $response, $args) { return $this->view->render($response, 'profile.html', [ 'name' => $args['name'] ]); })->setName('profile'); // Run app $app->run(); ``` Figure 6: Render template with slim/twig-view container service. ## 其他模版系统 并不限于使用 `Twig-View` 和 `PHP-View` 组件。你可以使用任意 PHP 模版系统,只要它能渲染你的模版,并最终输出到 PSR7 响应对象的 body 中。附加组件
最后更新于:2022-04-01 22:34:15
# 附加组件
';
在 Slim 中使用 Eloquent
最后更新于:2022-04-01 22:34:13
# 在 Slim 中使用 Eloquent
你可以使用 [Eloquent](https://laravel.com/docs/5.1/eloquent) 这种数据库 ORM 将你的 Slim 应用程序连接到数据库。
## 为你的应用程序添加 Eloquent
```
composer require illuminate/database "~5.1"
```
Figure 1: Add Eloquent to your application.
## 配置 Eloquent
在 Slim 的 setting 数组中添加数据库设置项。
```
[
// Slim Settings
'determineRouteBeforeAppMiddleware' => false,
'displayErrorDetails' => true,
'db' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]
],
];
```
Figure 2: Settings array.
在 `dependencies.php` 中,或者其他任意位置添加你的 Service Factories:
```
// Service factory for the ORM
$container['db'] = function ($container) {
$capsule = new \Illuminate\Database\Capsule\Manager;
$capsule->addConnection($container['settings']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
return $capsule;
};
```
Figure 3: Configure Eloquent.
## 传递数据表实例到控制器
```
$container[App\WidgetController::class] = function ($c) {
$view = $c->get('view');
$logger = $c->get('logger')
$table = $c->get('db')->table('table_name');
return new \App\WidgetController($view, $logger, $table);
};
```
Figure 4: Pass table object into a controller.
## 从控制器中查询数据表
```
view = $view;
$this->logger = $logger;
$this->table = $table;
}
public function __invoke(Request $request, Response $response, $args)
{
$widgets = $this->table->get();
$this->view->render($response, 'app/index.twig', [
'widgets' => $widgets
]);
return $response;
}
}
```
Figure 5: Sample controller querying the table.
### 使用 where 查询数据表
```
...
$records = $this->table->where('name', 'like', '%foo%')->get();
...
```
Figure 6: Query searching for names matching foo.
### 通过 id 查询数据表
```
...
$record = $this->table->find(1);
...
```
Figure 7: Selecting a row based on id.
## 了解更多
[Eloquent](https://laravel.com/docs/5.1/eloquent) 文档
';
检索当前路由
最后更新于:2022-04-01 22:34:10
# 检索当前路由
如果你需要在应用程序中获取当前的路由,你所需要做但就是,调用 HTTP 请求类的带有 `'route'` 参数的 `getAttribute` 方法,它将返回当前的路由,这个路由是 `Slim\Route` 类的实例。class.
可以使用 `getName()` 获取路由的名称,或者使用 `getMethods()`获取此路由支持的方法, etc。
Note: 如果你需要在 app 中间件中访问路由,必须在配置中将 `'determineRouteBeforeAppMiddleware'` 设置为 true。否则,`getAttribute('route')` 将会返回 null。该路由在路由中间件中永远可用。
Example:
```
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\App;
$app = new App([
'settings' => [
// Only set this if you need access to route within middleware
'determineRouteBeforeAppMiddleware' => true
]
])
// routes...
$app->add(function (Request $request, Response $response, callable $next) {
$route = $request->getAttribute('route');
$name = $route->getName();
$groups = $route->getGroups();
$methods = $route->getMethods();
$arguments = $route->getArguments();
// do something with that information
return $next($request, $response);
});
```
';
检索 IP 地址
最后更新于:2022-04-01 22:34:08
# 检索 IP 地址
检索客户端当前 IP 地址的最佳方式,是利用使用了类似 [rka-ip-address-middleware](https://github.com/akrabat/rka-ip-address-middleware) 这种组件的中间件。
这个组件可以通过 composer 来安装:
```
composer require akrabat/rka-ip-address-middleware
```
要使用这个组件,需要使用 `App` 注册中间件,这里提供了一个可信赖的代理列表(e.g. varnish 服务器), 如果你再使用它们:
```
$checkProxyHeaders = true;
$trustedProxies = ['10.0.0.1', '10.0.0.2'];
$app->add(new RKA\Middleware\IpAddress($checkProxyHeaders, $trustedProxies));
$app->get('/', function ($request, $response, $args) {
$ipAddress = $request->getAttribute('ip_address');
return $response;
});
```
这个中间件把客户端 IP 地址存储在一个 HTTP 请求属性中,所以需要通过 `$request->getAttribute('ip_address')` 来访问。
';
以 / 结尾的路由模式
最后更新于:2022-04-01 22:34:06
# 以 / 结尾的路由模式
[Edit This Page](https://github.com/slimphp/Slim-Website/tree/gh-pages/docs/cookbook/route-patterns.md)
Slim 处理带有斜线结尾的 URL 和不带斜线的 URL 的方式不同。意思就是 `/user` 和 `/user/` 不是一回事,它们可以有不同的回调。
如果你想通过重定向让所有以 `/` 结尾的 URL 和不以 `/` 结尾的 URL 相等,你可以添加这个中间件:
```
use Psr\Http\Message\RequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
$app->add(function (Request $request, Response $response, callable $next) {
$uri = $request->getUri();
$path = $uri->getPath();
if ($path != '/' && substr($path, -1) == '/') {
// permanently redirect paths with a trailing slash
// to their non-trailing counterpart
$uri = $uri->withPath(substr($path, 0, -1));
return $response->withRedirect((string)$uri, 301);
}
return $next($request, $response);
});
```
或者,也可以使用 [oscarotero/psr7-middlewares’ TrailingSlash](//github.com/oscarotero/psr7-middlewares#trailingslash) 中间件强制为所有 URL 添加结尾的斜线:
```
use Psr7Middlewares\Middleware\TrailingSlash;
$app->add(new TrailingSlash(true)); // true 则添加结尾斜线 (false 移除斜线)
```
';
烹饪书
最后更新于:2022-04-01 22:34:04
# 烹饪书
';
405 Not Allowed 处理器
最后更新于:2022-04-01 22:34:01
# 405 Not Allowed 处理器
[Edit This Page](https://github.com/slimphp/Slim-Website/tree/gh-pages/docs/handlers/not-allowed.md)
如果你的 Slim 框架应用程序有一个路由匹配到了当前的 HTTP 请求 URI **而非 HTTP 请求方法**,程序将调用 Not Allowed 处理器并返回一个 `HTTP/1.1 405 Not Allowed` 响应到 HTTP 客户端。
## 默认的 Not Allowed 处理器
每个 Slim 框架应用程序都有一个默认的 Not Allowed 处理器。该处理器将 HTTP 响应状态设置为 `405`,将内容类型设置为 `text/html`,它还会添加一个包含由逗号分隔的已被允许访问的 HTTP 方法组成的列表的 `Allowed:` HTTP 头它还会在 HTTP 响应体中写入一个简单的注释。
## 自定义 Not Allowed 处理器
Slim 框架应用程序的 Not Allowed 处理器是一个 Pimple 服务。你可以通过应用程序容器对自定义 Pimple factory 方法进行定义,来创建自定义的 Not Allowed 处理器取代默认的
```
// Create Slim
$app = new \Slim\App();
// get the app's di-container
$c = $app->getContainer();
$c['notAllowedHandler'] = function ($c) {
return function ($request, $response, $methods) use ($c) {
return $c['response']
->withStatus(405)
->withHeader('Allow', implode(', ', $methods))
->withHeader('Content-type', 'text/html')
->write('Method must be one of: ' . implode(', ', $methods));
};
```
> **注意** Check out [Not Found](/docs/handlers/not-found.html) docs for pre-slim creation method using a new instance of `\Slim\Container`
在这个例子中,我们定义了一个新的 `notAllowedHandler` factory ,它将返回一个 callable 。返回的 callable 接收两个参数:
1. 一个 `\Psr\Http\Message\ServerRequestInterface` 实例
2. 一个 `\Psr\Http\Message\ResponseInterface` 实例
3. 一个由已允许访问的 HTTP 方法名组成的数组
这个 callable **必须** 返回一个恰当的 `\Psr\Http\Message\ResponseInterface` 实例。
';
404 Not Found 处理器
最后更新于:2022-04-01 22:33:59
# 404 Not Found 处理器
[Edit This Page](https://github.com/slimphp/Slim-Website/tree/gh-pages/docs/handlers/not-found.md)
如果你的 Slim 应用程序没有可以匹配到当前 HTTP 请求 URI 的路由,程序会调用 Not Found 处理器,并返回一个 `HTTP/1.1 404 Not Found` 响应到 HTTP 客户端。
## 默认的 Not Found 处理器
每个 Slim 框架应用程序都有一个默认的 Not Found 处理器。这个处理器将响应状态设置为 `404`,并将内容类型设置为 `text/html` ,它将写入一个简单的异常信息到响应体。
## 自定义 Not Found 处理器
Slim 框架应用程序的 Not Found 处理器是一个 Pimple 服务。 你可以通过应用程序容器对自定义 Pimple factory 方法进行定义,来创建自定义的 Not Found 处理器取代默认的。
```
$c = new \Slim\Container(); //Create Your container
//Override the default Not Found Handler
$c['notFoundHandler'] = function ($c) {
return function ($request, $response) use ($c) {
return $c['response']
->withStatus(404)
->withHeader('Content-Type', 'text/html')
->write('Page not found');
};
};
//Create Slim
$app = new \Slim\App($c);
//... Your code
```
在这个例子中,我们定义了一个新的 `notFoundHandler` factory ,它将返回一个 callable 。返回的 callable 接收2个参数:
1. 一个 `\Psr\Http\Message\ServerRequestInterface` 实例
2. 一个 `\Psr\Http\Message\ResponseInterface` 实例
这个 callable **必须** 返回一个恰当的 `\Psr\Http\Message\ResponseInterface` 实例。
';
500 系统错误处理器
最后更新于:2022-04-01 22:33:57
# 500 系统错误处理器
[Edit This Page](https://github.com/slimphp/Slim-Website/tree/gh-pages/docs/handlers/error.md)
出问题了!你并不能预知错误,但你可以预料到。每个 Slime 框架应用程序都有一个错误处理器用于接收所有未抓取的 PHP 异常。这个错误处理器同样能接收当前 HTTP 的请求对象和响应对象。这个错误处理器必须预备并返回一个适当的响应对象,这个对象会被返回到 HTTP 客户端。
## 默认的错误处理器
默认的错误处理器非常基础。它将响应状态编码设置为 `500`,并将响应内容类型设置为 `text/html`,并将一个通用的错误信息加入到响应体中。
这个对于生产应用_大概_不太合适。强烈建议你实现专用于你的 Slim 应用程序的错误处理器。
默认的错误处理程序还可以包括详细的错误诊断信息。要启用这个功能你需要将 `displayErrorDetails` 设置为 true :
```
$configuration = [
'settings' => [
'displayErrorDetails' => true,
],
];
$c = new \Slim\Container($configuration);
$app = new \Slim\App($c);
```
## 自定义错误处理器
Slim 框架应用程序的错误处理器是一种 Pimple 服务。你可以通过应用程序容器对自定义 Pimple factory 方法进行定义,来创建自定义的错误处理器取代默认的。
有两种注入处理器的方法:
### Pre App
```
$c = new \Slim\Container();
$c['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
return $c['response']->withStatus(500)
->withHeader('Content-Type', 'text/html')
->write('Something went wrong!');
};
};
$app = new \Slim\App($c);
```
### Post App
```
$app = new \Slim\App();
$c = $app->getContainer();
$c['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) {
return $c['response']->withStatus(500)
->withHeader('Content-Type', 'text/html')
->write('Something went wrong!');
};
};
```
在这个例子中,我们定义了一个新的 `errorHandler` factory ,它将返回一个 callable 。返回的 callable 接收三个参数:
1. 一个 `\Psr\Http\Message\ServerRequestInterface` 实例
2. 一个 `\Psr\Http\Message\ResponseInterface` 实例
3. 一个 `\Exception` 实例
这个 callable **必须** 返回一个新的 `\Psr\Http\Message\ResponseInterface` 实例,对于给定的异常也是如此。
**务必注意**:下面这三个类型的异常不会被自定义的 `errorHandler` 处理:
* `Slim\Exception\MethodNotAllowedException`: 这个可以用自定义的 [`notAllowedHandler`](/docs/handlers/not-allowed.html) 来处理。
* `Slim\Exception\NotFoundException`: 这个可以用自定义的 [`notFoundHandler`](/docs/handlers/not-found.html) 来处理。
* `Slim\Exception\SlimException`: 这种类型的异常是 Slim 内置的,它的处理不能被覆写。
### 禁用
要想彻底地禁用 Slim 的错误处理器,只需从容器中移除错误处理器即可:
```
unset($app->getContainer()['errorHandler']);
```
现在,你需要负责处理所有异常了,因为 Slim 已经不再处理它们。
';
错误处理
最后更新于:2022-04-01 22:33:55
# 错误处理
';
路由
最后更新于:2022-04-01 22:33:52
# 路由
Slim 框架的路由基于 [nikic/fastroute](https://github.com/nikic/FastRoute) 组件进行构建,它格外地快速稳定。
## 如何创建路由
你在可以使用 `\Slim\App` 实例的代理(proxy)方法来定义应用程序路由。Slim 框架提供了最流行的HTTP方法。
### Get 路由
使用 Slim 应用程序的 `get()` 方法添加一个只处理 `GET` HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->get('/books/{id}', function ($request, $response, $args) {
// Show book identified by $args['id']
});
```
### POST 路由
使用 Slim 应用程序的 `post()` 方法添加一个只处理 `POST` HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->post('/books', function ($request, $response, $args) {
// Create new book
});
```
### PUT 路由
使用 Slim 应用程序的 `put()` 方法添加一个只处理 `PUT` HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->put('/books/{id}', function ($request, $response, $args) {
// Update book identified by $args['id']
});
```
### DELETE 路由
使用 Slim 应用程序的 `delete()` 方法添加一个只处理 `DELETE` HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->delete('/books/{id}', function ($request, $response, $args) {
// Delete book identified by $args['id']
});
```
### OPTIONS 路由
使用 Slim 应用程序的 `options()` 方法添加一个只处理 `OPTIONS` HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->options('/books/{id}', function ($request, $response, $args) {
// Return response headers
});
```
### PATCH 路由
使用 Slim 应用程序的 `patch()` 方法添加一个只处理 `PATCH` HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->patch('/books/{id}', function ($request, $response, $args) {
// Apply changes to book identified by $args['id']
});
```
### 任意路由
使用 Slim 应用程序的 `any()` 方法添加一个可以处理处理所有 HTTP 请求的路由。它接收两个参数:
1. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
2. 路由回调
```
$app = new \Slim\App();
$app->any('/books/[{id}]', function ($request, $response, $args) {
// Apply changes to books or book identified by $args['id'] if specified.
// To check which method is used: $request->getMethod();
});
```
记住,第二个参数是一个回调。你需要指定一个类(Class,需要一个 `__invoke()` 实现方法。)来替换闭包(Closure)。接着,你可以映射到其他位置:
```
$app->any('/user', 'MyRestfulController');
```
### 自定义路由
使用 Slim 应用程序的 `map()` 方法来添加一个可以处理多个 HTTP 请求方法的路由。它接收三个参数:
1. HTTP 方法的数组
2. 路由模式(带有可选的命名占位符)/The route pattern (with optional named placeholders)
3. 路由回调
```
$app = new \Slim\App();
$app->map(['GET', 'POST'], '/books', function ($request, $response, $args) {
// Create new book or list all books
});
```
## 路由回调
上述的每个路由方法都接收一个回调例程作为其最后一个参数。这个参数可以是任意 PHP 调用(callable),并且它默认接受三个参数。
**请求/Request**
第一个参数是一个 `Psr\Http\Message\ServerRequestInterface` 对象,表示当前的 HTTP 请求。
**响应/Response**
第二个参数是一个 `Psr\Http\Message\ResponseInterface` 对象,表示当前的 HTTP 响应。
**参数数组/Arguments**
第三个参数是一个关联数组,包含包含当前路由的命名占位符。
### 将内容写入响应
有两种方式可以将内容写入 HTTP 响应。第一钟,可以使用 `echo()` 简单地从路由回调中输出内容。其内容将会呗追加到当前 HTTP 请求对象中。第二种,你可以返回一个 `Psr\Http\Message\ResponseInterface` 对象。
### 闭包绑定/Closure binding
如果你使用一个`闭包(Closure)`实例作为路由回调,闭包的状态受`Container`实例约束。这意味着你将通过 `$this` 关键字访问闭包内部的 DI 容器实例:
```
$app = new \Slim\App();
$app->get('/hello/{name}', function ($request, $response, $args) {
// Use app HTTP cookie service
$this->get('cookies')->set('name', [
'name' => $args['name'],
'expires' => '7 days'
]);
});
```
## 路由策略
路由回调签名由路由策略决定。默认地,Slim 寄望路由回调来接收请求、响应和由路由占位符参数组成的数组。这称为请求响应策略(RequestResponse strategy)。然而,你可以通过使用另一个不同策略来改变这种寄望。例如,Slim 提供了一个另类的策略,叫做 RequestResponseArgs ,它接收请求和响应,加上由每一个路由占位符组成的单独参数。这里的例子展示了如何使用这个另类的策列;轻松替代了默认`\Slim\Container`提供的 `foundHandler` 依赖:
```
$c = new \Slim\Container();
$c['foundHandler'] = function() {
return new \Slim\Handlers\Strategies\RequestResponseArgs();
};
$app = new \Slim\App($c);
$app->get('/hello/{name}', function ($request, $response, $name) {
return $response->write($name);
});
```
通过实现 `\Slim\Interfaces\InvocationStrategyInterface` 你可以提供一个你自己的路由策略。
## 路由占位符
上诉的每个路由方法都会收到一个 URL 模式(URL pattern),它将与当前 HTTP 请求的 URI 相匹配。路由模式将使用命名占位符(named placeholder)来动态匹配 HTTP 请求的URI 片段。
### 格式
路由模式占位符起始与一个 `{`, 然后是占位符名称, 最后以 `}` 结束。这是一个名为 `name` 的占位符例子:
```
$app = new \Slim\App();
$app->get('/hello/{name}', function ($request, $response, $args) {
echo "Hello, " . $args['name'];
});
```
### 可选的分段 / Optional segments
使一个片段可选,只需用将其放在方括号中:
```
$app->get('/users[/{id}]', function ($request, $response, $args) {
// reponds to both `/users` and `/users/123`
// but not to `/users/`
});
```
支持多个可选参数嵌套:
```
$app->get('/news[/{year}[/{month}]]', function ($request, $response, $args) {
// reponds to `/news`, `/news/2016` and `/news/2016/03`
});
```
对于数目不确定的可选参数,可以这样做:
```
$app->get('/news[/{params:.*}]', function ($request, $response, $args) {
$params = explode('/', $request->getAttribute('params'));
// $params is an array of all the optional segments
});
```
在这个例子中,`/news/2016/03/20` 的 URI 将使得 `$params` 数组包含三个元素:`['2016', '03', '20']`.
### 正则表达式匹配
默认地,占位符被写在 `{}` 之中,并可以接收任意值。然而,占位符同样可以要求 HTTP 请求 URI 匹配特定的正则表达式。如果当前的HTTP 请求URI 不能与占位符的正则表达式匹配,路由路由将不会启动。下面这个示例占位符要求 `id` 必须是个数字:
```
$app = new \Slim\App();
$app->get('/users/{id:[0-9]+}', function ($request, $response, $args) {
// Find user identified by $args['id']
});
```
## 路由名称
应用程序的路由可以被指定一个名称。这非常有用,如果你想要使用路由的 `pathFor()` 方法以编程的形式生成一个特定路由的 URL 。上述的每个路由方法都会返回一个 `\Slim\Route` 对象,这个对象带来了 `setName()` 方法。
```
$app = new \Slim\App();
$app->get('/hello/{name}', function ($request, $response, $args) {
echo "Hello, " . $args['name'];
})->setName('hello');
```
使用应用程序路由的 `pathFor()` 方法,为已命名的路由生成一个 URL 。
```
echo $app->router->pathFor('hello', [
'name' => 'Josh'
]);
// Outputs "/hello/Josh"
```
路由的 `pathFor()` 方法接收两个参数:
1. 路由名称
2. 由路由模式占位符及替换值组成的关联数组。
## 路由组
为了帮助将路由整理成条理分明的路由组, `\Slim\App` 还提供了一个 `group()` 方法。 每个路由组的路由模式预置于路由或路由组中的路由组,路由组模式的任何占位符参数最终使得嵌套的路由都是可用的:
```
$app = new \Slim\App();
$app->group('/users/{id:[0-9]+}', function () {
$this->map(['GET', 'DELETE', 'PATCH', 'PUT'], '', function ($request, $response, $args) {
// Find, delete, patch or replace user identified by $args['id']
})->setName('user');
$this->get('/reset-password', function ($request, $response, $args) {
// Route for /users/{id:[0-9]+}/reset-password
// Reset the password for user identified by $args['id']
})->setName('user-password-reset');
});
```
记住,在路由组闭包的内部,使用 `$this` 替代 `$app` 。Slim 将闭包绑定到应用程序实例,就像路由回调的情况那样。
## 路由中间件
你可以将中间件与任意路由或路由组相连接。 [了解更多](/docs/concepts/middleware.html).
## 容器识别 / Container Resolution
你不必限于为路由定义函数。在 Slim 中,有一些不同的方式来定义你的路由行为函数。
除了函数外,你还可以使用: - 可调用的类 - `Class:method`
这个函数由 Slim 的 Callable 解角器(Resolver) 类提供支持。它将字符串入口(entry)转变为函数调用。例如:
```
$app->get('/home', '\HomeController:home');
```
在上面这段代码中,我们定义了一个 `/home` 路由,并告诉 Slim 执行 `\HomeController` 类中的 `home()` 方法。
Slim 首先在容器中寻找 `\HomeController` 的入口,如果找到了,它将使用该实例。否则,它将它的构造函数,并将容器作为第一个参数。一旦这个类的实例创建了,它将使用你已定义的策略(Strategy)去调用指定的方法。
作为另一种办法,你可以使用一个可调用的(invokable)类,比如:
```
class MyAction {
protected $ci;
//Constructor
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
public function __invoke($request, $response, $args) {
//your code
//to access items in the container... $this->ci->get('');
}
}
```
你可以这样使用这个累:
```
$app->get('/home', '\MyAction');
```
在更为传统的 MVC 方法中,你构建包含许多行为(actions)的控制器来替代只能处理一个行为的可调用类。
```
class MyController {
protected $ci;
//Constructor
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
public function method1($request, $response, $args) {
//your code
//to access items in the container... $this->ci->get('');
}
public function method2($request, $response, $args) {
//your code
//to access items in the container... $this->ci->get('');
}
public function method3($request, $response, $args) {
//your code
//to access items in the container... $this->ci->get('');
}
}
```
你可以这样使用控制器方法:
```
$app->get('/method1', '\MyController:method1');
$app->get('/method2', '\MyController:method2');
$app->get('/method3', '\MyController:method3');
```
';
HTTP 响应
最后更新于:2022-04-01 22:33:50
# HTTP 响应
Slim 应用程序的路由和中间件给出了一个 PSR 7 响应对象,它表示当前的 HTTP 响应将被返回给客户端。该响应对象遵循 [PSR 7 响应接口](http://www.php-fig.org/psr/psr-7/#3-2-1-psr-http-message-responseinterface)实现,因此你可以检查和操作该 HTTP 响应的状态、响应头和响应体。
## 如何获取 HTTP 响应对象
PSR 7 响应对象作为路由回调的第二个参数注入到 Slim 应用程序的路由中:
```
get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {
// Use the PSR 7 $response object
return $response;
});
$app->run();
```
Figure 1: Inject PSR 7 response into application route callback.
PSR 7 请求对象作为中间件 callable 的第二个参数注入到 Slim 应用程序的_中间件_:
```
add(function (ServerRequestInterface $request, ResponseInterface $response, callable $next) {
// Use the PSR 7 $response object
return $next($request, $response);
});
// Define app routes... $app->run();
```
Figure 2: Inject PSR 7 response into application middleware.
## HTTP 响应状态
每个 HTTP 响应都有一个数字 [状态编码](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)。状态编码用于识别返回客户端的 HTTP 响应的_类型_。PSR 7 响应对象的默认状态编码是 `200` (OK)。你可以像这样使用 `getStatusCode()` 方法获取 PSR 7 响应对象的状态编码:
```
$status = $response->getStatusCode();
```
Figure 3: Get response status code.
你可以拷贝一个 PSR 7 响应对象,并指定一个新的状态编码,像这样:
```
$newResponse = $response->withStatus(302);
```
Figure 4: Create response with new status code.
## HTTP 响应头
每个 HTTP 响应都有其相应的响应头。这些元数据描述了 HTTP 响应,但在响应体中不可见。Slim 的 PSR 7 响应对象提供了几种检查和操作响应头的方法。
### 获取所有响应头
使用 PSR 7 响应对象的 `getHeaders()` 方法提取所有 HTTP 响应头并存入一个关联数组中。该关联数组的键名即为响应头的名称,键值是与其响应头对应的值的字符串数组。
```
$headers = $response->getHeaders();
foreach ($headers as $name => $values) {
echo $name . ": " . implode(", ", $values);
}
```
Figure 5: Fetch and iterate all HTTP response headers as an associative array.
### 获取单个响应头
使用 PSR 7 响应对象的 `getHeader($name)` 方法获取单个响应头的值。它将返回指定响应头的值组成的数组。记住,单个 HTTP 响应不止一个值。
```
$headerValueArray = $response->getHeader('Vary');
```
Figure 6: Get values for a specific HTTP header.
同样,可以是 PSR 7 响应对象的 `getHeaderLine($name)` 方法获取指定响应头的所有值,由逗号隔开。不同于 `getHeader($name)` 方法,此方法返回的是由逗号隔开的字符串。
```
$headerValueString = $response->getHeaderLine('Vary');
```
Figure 7: Get single header's values as comma-separated string.
### 检查响应头
使用 PSR 7 响应对象的 `hasHeader($name)` 方法检查响应头存在与否。
```
if ($response->hasHeader('Vary')) {
// Do something
}
```
Figure 8: Detect presence of a specific HTTP header.
### 设置响应头
使用 PSR 7 响应对象的 `withHeader($name, $value)` 方法设置响应头的值。
```
$newResponse = $oldResponse->withHeader('Content-type', 'application/json');
```
Figure 9: Set HTTP header
**提示**响应对象是不可改的。此方法返回一个响应对象的_拷贝(copy)_,它拥有新的值。**此方法是破坏性的**,它替换了已有的同名响应头现有的值。
### 追加响应头/Append Header
使用 PSR 7 响应对象的 `withAddedHeader($name, $value)` 方法追加一个响应头的值。
```
$newResponse = $oldResponse->withAddedHeader('Allow', 'PUT');
```
Figure 10: Append HTTP header
**提示**不同于 `withHeader()` 方法,此方法是_追加(append)_新的值到响应头已有的值中。该响应对象是不可修改的。此方法返回一个已添加新值的该对象的_拷贝_。
### 移除响应头
使用 HTTP 响应对象的 `withoutHeader($name)` 方法移除响应头。
```
$newResponse = $oldResponse->withoutHeader('Allow');
```
Figure 11: Remove HTTP header
**提示**响应对象是不可改的。此方法返回一个带有追加的响应头的值的响应对象的_拷贝(copy)_。
## HTTP 响应体
HTTP 响应通常有一个响应体。Slim 提供了一个 PSR 7 响应对象,你可以用它检查或操作可能会有的响应体。
类似 PSR 7 请求对象,PSR 7 响应对象将响应体作为`\Psr\Http\Message\StreamInterface` 的实例来实现。你可以使用 PSR 7 响应对象的 `getBody()` 方法来获取 HTTP 响应体 `StreamInterface` 的实例。该 `getBody()` 方法完美适用于未知大小或对于可用内容来说太大的输出(outgoing) HTTP 响应。
```
$body = $response->getBody();
```
Figure 12: Get HTTP response body
所得的 `\Psr\Http\Message\StreamInterface` 实例提供以下方法来读取、迭代、写入到潜在的 PHP`资源(resource)`。
* `getSize()`
* `tell()`
* `eof()`
* `isSeekable()`
* `seek()`
* `rewind()`
* `isWritable()`
* `write($string)`
* `isReadable()`
* `read($length)`
* `getContents()`
* `getMetadata($key = null)`
大多数情况下,你会需要写入到 PSR 7 响应对象。你可以像这样使用 `write()` 方法将内容写入到 `StreamInterface` 实例:
```
$body = $response->getBody();
$body->write('Hello');
```
Figure 13: Write content to the HTTP response body
你同样可以完整新建一个 `StreamInterface` 实例来_替换_ PSR 7 响应对象的响应体。这玩意特别有用,尤其是你想要从远程地址传输内容到 HTTP 响应中时(例如,文件系统或远程API)。你可以使用 `withBody(StreamInterface $body)` 方法替换 PSR 7 响应对象的响应体。它的参数**必须**是 `\Psr\Http\Message\StreamInterface` 的实例。
```
$newStream = new \GuzzleHttp\Psr7\LazyOpenStream('/path/to/file', 'r');
$newResponse = $oldResponse->withBody($newStream);
```
Figure 14: Replace the HTTP response body
**提示**响应对象不可改变。这个方法返回的是包含新响应体的对象的_拷贝(copy)_。
## 返回 JSON
Slim 的响应对象拥有一个自定义方法 `withJson($data, $status, $encodingOptions)` 来帮助优化返回 JSON 数据的过程。
其中 `$data` 参数包含你希望返回的 JSON 的数据结构。`$status` 是可选的,并能返回一个自定义的 HTTP 代码。`$encodingOptions` 是可选的,它是与[`json_encode()`](http://php.net/manual/en/function.json-encode.php) 相同的编码选项。
最简单的形式,可以返回带默认的 200 HTTP 状态代码的 JSON 数据。
```
$data = array('name' => 'Bob', 'age' => 40);
$newResponse = $oldResponse->withJson($data);
```
Figure 15: Returning JSON with a 200 HTTP status code.
还可以返回带有自定义 HTTP 状态码的 JSON 数据。
```
$data = array('name' => 'Rob', 'age' => 40);
$newResponse = $oldResponse->withJson($data, 201);
```
Figure 16: Returning JSON with a 201 HTTP status code.
HTTP 响应的 `Content-Type` 自动设置为 `application/json;charset=utf-8`。
如果 JSON 存在数据编码问题,`\RuntimeException($message, $code)`将抛出异常,包含将 [`json_last_error_msg()`](http://php.net/manual/en/function.json-last-error-msg.php) 的值作为 `$message`的值以及将 [`json_last_error()`](http://php.net/manual/en/function.json-last-error.php) 作为 the `$code`的值。
**提示**响应对象是不可改的。此方法返回一个响应对象的_拷贝(copy)_,该拷贝带有新的 Content-Type 头。**该方法是毁灭性的**,它将_替换(replaces)_已存在的 Content-Type 头。当 `withJson()` 被调用,如果传递了 $status ,HTTP 状态码也会被替换。
';
HTTP 请求
最后更新于:2022-04-01 22:33:48
# HTTP 请求
Slim 应用程序的路由和中间件给出了一个 PSR 7 请求对象,它表示当前的 HTTP 请求是由 Web 服务器 接收到的。该请求对象遵循 [PSR 7 服务器请求接口(ServerRequestInterface )](http://www.php-fig.org/psr/psr-7/#3-2-1-psr-http-message-serverrequestinterface) 实现,因此你可以检查和操作该 HTTP 请求对象的方法、头和体。
## 如何获取请求对象
该 PSR 7 请求对象作为路由回调的第一个参数注入到你的 Slim 应用程序的路由中,像这样:
```
get('/foo', function (ServerRequestInterface $request, ResponseInterface $response) {
// 使用 PSR 7 $request 对象
return $response;
});
$app->run();
```
Figure 1: 将 PSR 7 请求注入到应用程序的路由回调中。
该 PSR 7 请求对象作为中间件 callable 的第一个参数注入到 Slim 应用程序的_中间件_中,像这样:
```
add(function (ServerRequestInterface $request, ResponseInterface $response, callable $next) {
// Use the PSR 7 $request object
return $next($request, $response);
});
// Define app routes...
$app->run();
```
Figure 2: 注入 PSR 7 请求到应用程序中间件
## 请求的方法
每个 HTTP 请求都有相应的方法,通常是这些中的一个:
* GET
* POST
* PUT
* DELETE
* HEAD
* PATCH
* OPTIONS
你可以恰当地使用 `getMethod()` 请求对象方法来检查 HTTP 请求方法。
```
$method = $request->getMethod();
```
由于这是一个常见的功能,Slim 的内置 PSR 7 实现方法也提供了这些专有方法,它们返回 `true` 或 `false` 。
* `$request->isGet()`
* `$request->isPost()`
* `$request->isPut()`
* `$request->isDelete()`
* `$request->isHead()`
* `$request->isPatch()`
* `$request->isOptions()`
还可以伪造或_覆写_这些 HTTP 请求方法。这非常有用,例如你需要在一个只支持 `GET` 或 `POST` 请求的传统浏览器中模拟一个 `PUT` 请求。
有两种方法来覆写 HTTP 请求方法。你可以在一个 `POST` 请求体中引入(include)一个 `_METHOD` 参数。该 HTTP 请求必须使用 `application/x-www-form-urlencoded` 内容类型(content type)。
```
POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 22
data=value&_METHOD=PUT
```
Figure 3: 使用 _METHOD 参数覆写 HTTP 请求。
你也可以使用自定义的 `X-Http-Method-Override` HTTP请求头来覆写 HTTP 请求方法。这个方式可以用于任意 HTTP 请求内容类型(content type)。
```
POST /path HTTP/1.1
Host: example.com
Content-type: application/json
Content-length: 16
X-Http-Method-Override: PUT
{"data":"value"}
```
Figure 4: 使用 X-Http-Method-Override 请求头覆写 HTTP 方法。
你可以使用 PSR 7 请求对象的方法 `getOriginalMethod()` 来提取_原有_ (不是覆写后的)的 HTTP 方法。
## 请求 URI
每个 HTTP 请求都有一个识别被请求的应用程序资源的 URI 。HTTP 请求 URI 分为这几部分:
* Scheme (e.g. `http` or `https`)
* Host (e.g. `example.com`)
* Port (e.g. `80` or `443`)
* Path (e.g. `/users/1`)
* Query string (e.g. `sort=created&dir=asc`)
你可以使用 `getUri()` 方法来提取 PSR 7 请求对象的 URI :
```
$uri = $request->getUri();
```
PSR 7 请求对象的 URI 本身就是一个对象,提供了以下方法来检查 HTTP 请求的 URL :
* `getScheme()`
* `getAuthority()`
* `getUserInfo()`
* `getHost()`
* `getPort()`
* `getPath()`
* `getBasePath()`
* `getQuery()` <small>(返回整个查询字符串,e.g. `a=1&b=2`)</small>
* `getFragment()`
* `getBaseUrl()`
**基准路径**如果你的 Slim 应用程序的前端控制器放置在文件根目录的物理子目录中,你可以使用 URI 对象的 `getBasePath()` 方法来提取 HTTP 请求的物理基准路径(相对于文件根目录)。如果 Slim 应用程序安装在文件根目录的最上层目录中,它将返回一个空字符串。
## 请求头
每个 HTTP 请求都有请求头。这些元数据描述了 HTTP 请求,但在请求体中不可见。Slim 的 PSR 7 请求对象提供了几个检查请求头的方法。
### 获取所有请求头
使用 PSR 7 请求对象的`getHeaders()`方法提取所有 HTTP 请求头并放入一个关联数组中。此关联数组的键值是请求头的名称,其值是各请求头对应的由字符串值组成的数值数组。
```
$headers = $request->getHeaders();
foreach ($headers as $name => $values) {
echo $name . ": " . implode(", ", $values);
}
```
Figure 5: 提取并迭代所有 HTTP 请求头作为一个关联数组。
### 获取单个请求头
你可以使用 PSR 7 请求对象的 `getHeader($name)` 方法获取一个单独的请求头的值。它将返回一个由指定请求头名称对应的值组成的数组。记住,单独的请求头可不一定只有一个值。
```
$headerValueArray = $request->getHeader('Accept');
```
Figure 6: 获取指定 HTTP 请求的值。
你同样可以使用 PSR 7 请求对象的 `getHeaderLine($name)` 方法提取指定请求头的值,并以逗号分隔。这不同于 `getHeader($name)` 方法,这个方法返回一个由逗号分隔的字符串。
```
$headerValueString = $request->getHeaderLine('Accept');
```
Figure 7: 获取单个请求头的值,返回逗号分隔的字符串。
### 检测请求头
使用 PSR 7 请求对象的 `hasHeader($name)` 方法检查某请求头是否存在。
```
if ($request->hasHeader('Accept')) {
// Do something
}
```
Figure 8: Detect presence of a specific HTTP request header.
## 请求体
每个 HTTP 请求都有一个请求体。如果你的 Slim 应用程序是通过 JSON 或 XML 数据进行通信的,你可以使用 PSR 7 请求对象的 `getParsedBody()` 方法将 HTTP 请求体解析成原生 PHP 格式。Slim 可以解析 JSON, XML, 和 URL-encoded 数据,开箱即用。
```
$parsedBody = $request->getParsedBody();
```
Figure 9: Parse HTTP request body into native PHP format
* JSON 请求通过 `json_decode($input)` 转换成 PHP 对象。
* XML 请求通过 `simplexml_load_string($input)` 转换成 `SimpleXMLElement` 。
* URL-encoded 请求通过 `parse_str($input)` 转换成 PHP 数组。
从技术上说,Slim 的 PSR 7 请求对象将 HTTP 请求体表示为 `\Psr\Http\Message\StreamInterface` 的一个实例。你可以通过使用 PSR 7 请求对象的 `getBody()` 方法获取 HTTP 请求体的 `StreamInterface` 实例。该 `getBody()` 方法在处理未知大小或对于可用内存来说太大的 HTTP 请求时,是个很好的方法。
```
$body = $request->getBody();
```
Figure 10: 获取 HTTP 请求体
生成的 `\Psr\Http\Message\StreamInterface` 实例提供了以下方法来读取或迭代未知的 `资源(resource)`。
* `getSize()`
* `tell()`
* `eof()`
* `isSeekable()`
* `seek()`
* `rewind()`
* `isWritable()`
* `write($string)`
* `isReadable()`
* `read($length)`
* `getContents()`
* `getMetadata($key = null)`
## 请求助手/Request Helpers
Slim 的 PSR 7 请求实现方法提供了额外的专有方法来帮助你进一步检查 HTTP 请求。
### 检测 XHR 请求
你可以使用请求对象的 `isXhr()` 方法来检测 XHR 请求。该方法检测 `X-Requested-With` 请求头,并确保它的值是 `XMLHttpRequest`。
```
POST /path HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 7
X-Requested-With: XMLHttpRequest
foo=bar
```
Figure 11: XHR 请求示例.
```
if ($request->isXhr()) {
// Do something
}
```
### 内容类型/Content Type
你可以使用请求对象的 `getContentType()` 方法提取 HTTP 请求的内容类型。它将通过 HTTP 客户端返回 `Content-Type` 头的完整值。
```
$contentType = $request->getContentType();
```
### 媒体类型/Media Type
你或许不会想要完整的 `Content-Type` 头。加入说你只想要媒体类型?你可以使用响应对象的 `getMediaType()`方法取得 HTTP 请求的媒体类型。
```
$mediaType = $request->getMediaType();
```
可以使用请求对象的 `getMediaTypeParams()` 方法,以关联数组的形式获取附加的没提类型参数。
```
$mediaParams = $request->getMediaTypeParams();
```
### 字符集/Character Set
HTTP 请求字符集是最常用的媒体类型参数之一。请求对象提供了一个专用的方法来获取这个媒体类型参数。
```
$charset = $request->getContentCharset();
```
### 内容长度/Content Length
使用请求对象的 `getContentLength()` 方法获取 HTTP 请求的内容长度。
```
$length = $request->getContentLength();
```
### 路由对象/Route Object
有时在中间件中你需要路由的参数。
在这个例子中,我们首先检查用户是否登录,然后检查用户有否有权限浏览他们正试图浏览的视频。
```
$app->get('/course/{id}', Video::class.":watch")->add(Permission::class)->add(Auth::class);
//.. In the Permission Class's Invoke
/** @var $route \Slim\Route */
$route = $request->getAttribute('route');
$courseId = $route->getArgument('id');
```
## 媒体类型解析
如果 HTTP 请求的媒体类型被识别,将通过 `$request->getParsedBody()` 解析成为结构化的可用数据。通常是解析成一个数组,或者是 XML 媒体类型的对象。
以下媒体类型是可识别和解析的:
* application/x-www-form-urlencoded’
* application/json
* application/xml & text/xml
如果你想要用 Slim 来解析其它媒体类型,那么你可以自行解析原生的 HTTP 请求体,或者新注册一个媒体解析器。媒体解析器都是简单的 callable ,它接收一个 `$input` 字符串并返回一个解析后的对象或数组。
在应用程序或者路由中间件中注册新的媒体解析器。注意,你必须在首次尝试访问解析后的 HTTP 请求体前注册该解析器。
例如,要自动解析带有 `text/javascript` 内容类型的 JSON,你可以像这样在中间件中注册媒体解析器:
```
// 添加中间件
$app->add(function ($request, $response, $next) {
// add media parser
$request->registerMediaTypeParser(
"text/javascript",
function ($input) {
return json_decode($input, true);
}
);
return $next($request, $response);
});
```
';
Application
最后更新于:2022-04-01 22:33:46
# Application
Application ,(或者 `Slim\App` ) 是你的 Slim 应用程序的入口,它被用于注册那些链接到回调和控制器的路由。
```
// 实例化 App 对象
$app = new \Slim\App();
// 添加路由回调
$app->get('/', function ($request, $response, $args) {
return $response->withStatus(200)->write('Hello World!');
});
// 运行应用
$app->run();
```
## Application 配置
Application 只接收一个参数。该参数可以是[容器](/docs/concepts/di.html)实例或者用于配置自动创建的默认容器的数组。
Slim 还用到了一系列的设置项。它们被存放在 `settings` 配置关键字中。你还可以添加你的应用程序私有的设置项。
例如,我们可以将 Slim的设置项 `displayErrorDetails` 设置为 true,并配置 Monolog,像这样:
```
$config = [
'settings' => [
'displayErrorDetails' => true,
'logger' => [
'name' => 'slim-app',
'level' => Monolog\Logger::DEBUG,
'path' => __DIR__ . '/../logs/app.log',
],
],
];
$app = new \Slim\App($config);
```
### 获取 Settings
由于设置项都被存放在依赖注入容器中,所以你可以通过容器工厂方法(container factories)的 `settings` 关键字来访问它们。例如:
```
$settings = $container->get('settings')['logger'];
```
还可以在路由回调(route callable)中通过 `$this` 来访问它们:
```
$app->get('/', function ($request, $response, $args) {
$loggerSettings = $this->get('settings')['logger'];
// ...
});
```
## Slim 的默认设置
Slim 拥有以下默认设置,你可以按需覆写它们:
`httpVersion`
[HTTP 响应](/docs/objects/response.html)对象使用的 HTTP 协议版本
(默认: `'1.1'`)
`responseChunkSize`
从响应体读取并发送到浏览器的数据包的大小。
(默认: `4096`)
`outputBuffering`
当设置为 `false` 时,那么没有输出缓冲被启用。如果 `'append'` 或 `'prepend'`,那么任意 `echo` 或 `print` 状态都会被捕获,并且会 appended 或 prepended 到从路由回调(route callable)中返回的响应中。
(默认: `'append'`)
`determineRouteBeforeAppMiddleware`
当设置为 true 时,那么在中间件执行前即已确定路由是否正确。这意味着你如果有需要,可以在中间件中检查路由参数。
(默认: `false`)
`displayErrorDetails`
当设置为 true 时, 关于异常的附加信息都会通过 [默认的错误处理器](/docs/handlers/error.html)显示出来。
(默认: `false`)
';
依赖容器(Dependency Container)
最后更新于:2022-04-01 22:33:43
# 依赖容器(Dependency Container)
Slim 使用依赖容器来准备、管理和注入应用程序的相关依赖。Slim 支持 [Container-Interop](https://github.com/container-interop/container-interop) 接口实现的容器。你可以使用 Slim 的内置容器(基于 [Pimple](http://pimple.sensiolabs.org/))或者第三方的容器,比如 [Acclimate](https://github.com/jeremeamia/acclimate-container) 或 [PHP-DI](http://php-di.org/)。
## 如何使用容器
你并不_必须_提供一个依赖容器。如果你提供了,那么,你必须注入此容器的实例到 Slim 应用程序的构造函数中。
```
$container = new \Slim\Container;
$app = new \Slim\App($container);
```
你可以显式或隐式地从依赖容器中获取服务。你可以像下面这样子从 Slim 应用程序的路由中获取一个显示的容器实例。
```
/**
* Example GET route
*
* @param \Psr\Http\Message\ServerRequestInterface $req PSR7 request
* @param \Psr\Http\Message\ResponseInterface $res PSR7 response
* @param array $args Route parameters
*
* @return \Psr\Http\Message\ResponseInterface
*/
$app->get('/foo', function ($req, $res, $args) {
$myService = $this->get('myService');
return $res;
});
```
你可以这样隐式地从容器中取得服务:
```
/**
* Example GET route
*
* @param \Psr\Http\Message\ServerRequestInterface $req PSR7 request
* @param \Psr\Http\Message\ResponseInterface $res PSR7 response
* @param array $args Route parameters
*
* @return \Psr\Http\Message\ResponseInterface
*/
$app->get('/foo', function ($req, $res, $args) {
$myService = $this->myService;
return $res;
});
```
Slim uses `__get()` and `__isset()` magic methods that defer to the application’s container for all properties that do not already exist on the application instance.
## 必需的服务
你的容器必须实现这些必需的服务。如果你使用的是 Slim 内置的容器,这些服务都是已经准备好了的。如果你选择使用第三方容器,那么你必须自己来实现这些服务。
settings
应用程序设置项的关联数组(Associative array),包括以下关键字:
* `httpVersion`
* `responseChunkSize`
* `outputBuffering`
* `determineRouteBeforeAppMiddleware`.
* `displayErrorDetails`.
environment
`\Slim\Interfaces\Http\EnvironmentInterface` 的实例.
request
`\Psr\Http\Message\ServerRequestInterface`的实例.
response
`\Psr\Http\Message\ResponseInterface`的实例.
router
`\Slim\Interfaces\RouterInterface`的实例.
foundHandler
`\Slim\Interfaces\InvocationStrategyInterface` 的实例.
phpErrorHandler
PHP 7 错误被抛出时调用的 Callable。这个 callable **必须**返回一个 `\Psr\Http\Message\ResponseInterface` 的实例,并接收三个参数:
1. `\Psr\Http\Message\ServerRequestInterface`
2. `\Psr\Http\Message\ResponseInterface`
3. `\Error`
errorHandler
抛出异常时调用的 Callable。这个 callable **必须**返回一个 `\Psr\Http\Message\ResponseInterface` 的实例,并接收三个参数:
1. `\Psr\Http\Message\ServerRequestInterface`
2. `\Psr\Http\Message\ResponseInterface`
3. `\Exception`
notFoundHandler
如果当前的 HTTP 请求 URI 未能匹配到应用程序路由,则调用这个 Callable。这个 callable **必须**返回一个 `\Psr\Http\Message\ResponseInterface` 的实例,并接收三个参数:
1. `\Psr\Http\Message\ServerRequestInterface`
2. `\Psr\Http\Message\ResponseInterface`
notAllowedHandler
如果一个应用程序路由匹配到当前 HTTP 请求的路径而不是它的方法,则调用这个 Callable。这个 callable **必须** 返回一个 `\Psr\Http\Message\ResponseInterface` 的实例并接收三个参数:
1. `\Psr\Http\Message\ServerRequestInterface`
2. `\Psr\Http\Message\ResponseInterface`
3. Array of allowed HTTP methods
callableResolver
`\Slim\Interfaces\CallableResolverInterface` 的实例.
';
中间件
最后更新于:2022-04-01 22:33:41
# 中间件
你可以在你的 Slim 应用_之前(before)_ 和 _之后(after)_ 运行代码来处理你认为合适的请求和响应对象。这就叫做_中间件_。为什么要这么做呢?比如你想保护你的应用不遭受跨站请求伪造。也许你想在应用程序运行前验证请求。中间件对这些场景的处理简直完美。
## 什么是中间件?
从技术上来讲,中间件是一个接收三个参数的_可回调(callable)_对象:
1. `\Psr\Http\Message\ServerRequestInterface` - PSR7 请求对象
2. `\Psr\Http\Message\ResponseInterface` - PSR7 响应对象
3. `callable` - 下一层中间件的回调对象
它可以做任何与这些对象相应的事情。唯一硬性要求就是中间件**必须**返回一个 `\Psr\Http\Message\ResponseInterface` 的实例。 每个中间件都 **应当**调用下一层中间件,并讲请求和响应对象作为参数传递给它(下一层中间件)。
## 中间件是如何工作的?
不同的框架使用中间件的方式不同。在 Slim 框架中,中间件层以同心圆的方式包裹着核心应用。由于新增的中间件层总会包裹所有已经存在的中间件层。当添加更多的中间件层时同心圆结构会不断的向外扩展。
当 Slim 应用运行时,请求对象和响应对象从外到内穿过中间件结构。它们首先进入最外层的中间件,然后然后进入下一层,(以此类推)。直到最后它们到达了 Slim 应用程序自身。在 Slim 应用分派了对应的路由后,作为结果的响应对象离开 Slim 应用,然后从内到外穿过中间件结构。最终,最后出来的响应对象离开了最外层的中间件,被序列化为一个原始的 HTTP 响应消息,并返回给 HTTP 客户端。下图清楚的说明了中间件的工作流程:
![Middleware architecture](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-23_5629d1b6da0db.png)
## 如何编写中间件?
中间件是一个接收三个参数的可回调(callable)对象,三个参数是:请求对象、响应对象以及下一层中间件。中间件**必须**返回一个 `\Psr\Http\Message\ResponseInterface` 的实例。
### 闭包中间件示例。
这个示例中间件是一个闭包。
```
getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
};
```
### 可调用类的中间件示例。
这个示例中间件是一个实现了 `__invoke()` 魔术方法的可调用类。
```
getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
}
}
```
要使用这个类作为中间件,你可以使用 `->add( new ExampleMiddleware() );` 函数连接在 `$app`, `Route`, 或 `group()` 之后,在下面的代码中,它们中的任意一个都可以表示 $subject。
```
$subject->add( new ExampleMiddleware() );
```
## 如何添加中间件?
你可以在 Slim 应用中添加中间件,或者添加到一个单独的 Slim 应用路由,或者是路由组。所有场景都能接受相同的中间件和实现相同的中间件接口。
### 应用程序中间件
应用程序中间件被每个_传入(incoming)_ HTTP 请求调用。应用中间件可以通过 Slim 应用实例的 `add()` 方法来添加。下面这是一个添加上面那个闭包中间件的例子:
```
add(function ($request, $response, $next) {
$response->getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
});
$app->get('/', function ($req, $res, $args) {
echo ' Hello ';
});
$app->run();
```
输出的 HTTP 响应主体为:
```
BEFORE Hello AFTER
```
### 路由中间件
路由中间件_只有_在当前 HTTP 请求的方法和 URI 都与中间件所在路由相匹配时才会被调用。路由中间件会在你调用了任意 Slim 应用的路由方法(e.g., `get()` 或 `post()`)后被立即指定。 每个路由方法返回一个 `\Slim\Route` 的实例,这个类提供了相同的中间件接口作为 Slim 应用的实例。使用路由实例的 `add()` 方法将中间件添加到路由中。下面这是一个添加上面那个闭包中间件的例子:
```
getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
};
$app->get('/', function ($req, $res, $args) {
echo ' Hello ';
})->add($mw);
$app->run();
```
输出的 HTTP 响应主体为:
```
BEFORE Hello AFTER
```
### Group Middleware
除了整个应用,以及标准的路由可用接收中间件,还有 `group()` 多路由定义功能同样允许在其内部存在独立的路由。路由组中间件_只有_在其路由与已定义的 HTTP 请求和 URI 中的一个相匹配时才会被调用。要在回调中添加中间件,以及整组中间件,通过在 `group()` 方法后面连接 `add()` 来设置。
下面的示例程序,在一组 URL 处理程序(url-handlers)中使用了回调中间件。
```
get('/', function ($request, $response) {
return $response->getBody()->write('Hello World');
});
$app->group('/utils', function () use ($app) {
$app->get('/date', function ($request, $response) {
return $response->getBody()->write(date('Y-m-d H:i:s'));
});
$app->get('/time', function ($request, $response) {
return $response->getBody()->write(time());
});
})->add(function ($request, $response, $next) {
$response->getBody()->write('It is now ');
$response = $next($request, $response);
$response->getBody()->write('. Enjoy!');
return $response;
});
```
在调用 `/utils/date` 方法时,将输出类似下面这样子的字符串:
```
It is now 2015-07-06 03:11:01\. Enjoy!
```
访问 `/utils/time` 将会输出类似下面这样子的字符串:
```
It is now 1436148762\. Enjoy!
```
但访问 `/` _域名根目录(domain-root)_时,将会有如下输出,因为并没有分配中间件。
```
Hello World
```
';