本讲的内容为请求对象(Request)的概念和使用方法
#### 请求变量
ThinkPHP5使用了请求对象(由`think\Request`类实现)来记录当前请求的相关信息和执行相关操作,而获取请求变量是请求对象的一个重要用途。
例如,在原生PHP开发中经常会被使用的 `$_GET`、`$_POST`、`$_SESSION`和`$_COOKIE`等变量都被统一封装到请求对象的方法里面,取而代之的是使用请求对象的`get()`、`post()`、`session()`和 `cookie()`方法来获取当前请求的系统变量。
为什么要用请求对象的方法来替代原生的系统变量呢?
首先直接操作系统变量存在很多的不便和不足,例如大写变量、不支持过滤和批量读取、排除等等细节; 其次,也不利于单元测试。
在控制器操作方法中获取请求变量的方法如下:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 获取当前的GET变量
dump(Request::instance()->get());
// 或者使用助手函数
dump(request()->get());
}
}
~~~
`request函数`是系统提供的一个简化读取的助手函数,用于获取当前请求对象的单例。在ThinkPHP5中,你不需要根据当前请求类型来判断使用get()还是post()方法(事实上这是一个很愚蠢的做法),框架提供了一个专门的param()方法来统一获取当前请求的变量,会自动识别当前的请求类型(支持`PUT/DELETE`等所有的请求类型)获取不同的请求变量,例如:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 获取当前的请求变量
dump(Request::instance()->param());
// 或者使用助手函数
dump(request()->param());
}
}
~~~
获取单个变量的方式
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 获取当前的请求变量
dump(Request::instance()->param('name'));
// 或者使用助手函数
dump(request()->param('name'));
}
}
~~~
所以,官方的建议是最好弃用`get()`、`post()` 这些方法,直接使用`param()`方法。
`param`方法的优势包括:支持`统一过滤`以及更方便的`调用多级变量`,下面举个例子说明。
框架默认并没有设置任何的过滤机制,请根据应用的需要设置全局的过滤规则。
首先设置全局过滤规则如下:
~~~
// 默认全局过滤方法
'default_filter' => 'htmlspecialchars',
~~~
控制器的hello方法改为:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 获取当前的请求变量
dump(Request::instance()->param('data.name', '', 'strtoupper'));
// 或者使用助手函数
dump(request()->param('data.name', '', 'strtoupper'));
}
}
~~~
然后再来测试下数据(注意,data传入一个数组的正确姿势):
![](https://box.kancloud.cn/625a13f9440cb8e2c41db32d5232142c_1389x1043.png)
如果要判断某个请求变量是否存在,可以使用:
~~~
<?php
namespace app\index\controller;
use think\Request;
class Index
{
public function hello()
{
// 判断变量是否存在(当前请求变量中)
dump(request()->has('name'));
// 判断是否存在get变量
dump(request()->has('name', 'get'));
// 或者使用助手函数
dump(input('?name'));
dump(input('?get.name'));
}
}
~~~
为了方便使用,系统还提供了获取多个变量的方法(不是获取所有变量):
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 获取请求变量中的id和name变量
dump(request()->only('id,name'));
// 获取请求变量中的id以外的变量
dump(request()->except('id'));
}
}
~~~
only表示只获取部分变量,except表示获取除了排除变量之外的所有变量,测试效果如下:
![](https://box.kancloud.cn/adfb1bf05f268ca0e278621a04f63f4f_1389x1043.png)
请求对象提供的用于变量获取的方法:
|方法| 描述|
|--|--|
|has |判断请求变量是否存在|
|param |获取当前请求的变量|
|get 获取 |$_GET变量|
|post |获取 $_POST 变量|
|put |获取 PUT 变量|
|delete |获取 DELETE 变量|
|session |获取 $_SESSION变量|
|cookie |获取 $_COOKIE 变量|
|request |获取 $_REQUEST 变量|
|server |获取 $_SERVER 变量|
|env |获取 $_ENV变量|
|route |获取 路由(包括PATHINFO) 变量|
|file |获取 $_FILES变量|
|only |指定允许变量名获取请求变量|
|except |指定排除变量名获取请求变量|
#### 请求信息
每次请求还包含了URL信息、路由信息、模块/控制器/操作名称、客户端请求IP、请求类型以及请求头信息等等,我们都称之为请求信息。
使用`param方法`获取请求变量无需判断当前的请求类型,如果你确实需要获取当前的请求类型,则可以使用method方法。
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 获取当前请求类型
return '当前请求类型:' . request()->method();
}
}
~~~
使用postman进行get请求访问: `http://tp5.com/index/index/hello`
页面输出结果为:
![](https://box.kancloud.cn/d1b25cbd63a9642290839f402fd0f70c_1389x1043.png)
使用put请求访问后页面输出:
![](https://box.kancloud.cn/92cc9bccf94e28e8777539d872d0a39a_1389x1043.png)
如果你没有安装postman,那么可以用ThinkPHP5的请求伪装功能来进行请求测试。
创建一个测试表单如下:
~~~
<form action="/index/index/hello" method="post" >
<input type="hidden" name="_method" value="put">
<input type="submit" value="发起模拟请求">
</form>
~~~
为了更好的演示效果,我们调整下控制器的hello方法:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 获取当前请求类型
return '模拟请求类型:' . request()->method() . '<br/>实际请求类型:' . request()->method(true);
}
}
~~~
`method(true)`表示获取实际的请求类型。
点击发起模拟请求按钮后页面输出:
~~~
模拟请求类型:PUT
实际请求类型:POST
~~~
有些时候,我们需要判断当前的请求是否为`ajax`,就可以使用`isAjax`方法来判断:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 判断是否ajax提交
return '是否AJAX请求:' . var_export(request()->isAjax(), true);
}
}
~~~
![](https://box.kancloud.cn/6dbc5fe9cf9151930ee7b02fa84751e2_860x617.png)
请求对象中关于请求信息的方法包括:
|方法 |描述|
|--|--|
|method |获取当前请求类型|
|isGet |判断是否get请求|
|isPost |判断是否post请求|
|isPut |判断是否put请求|
|isDelete |判断是否delete请求|
|isHead |判断是否为head请求|
|isPatch |判断是否为patch请求|
|isOptions |判断是否为options请求|
|time |获取当前请求的请求时间|
|isSsl |判断是否为https请求|
|isAjax |判断是否为ajax请求|
|isPjax |判断是否为pjax请求|
|ip |判断当前访问的客户端IP|
|isMobile |判断是否为手机访问|
|module |获取请求的模块名|
|controller |获取请求的控制器名|
|action |获取请求的操作名|
|host |获取请求的host地址|
|port |获取请求的端口地址|
|protocol |获取请求的协议|
|remotePort |获取请求的REMOTE_PORT地址|
|header |获取当前请求头信息|
|contentType |获取当前请求的内容类型(V5.0.5+)|
#### 请求缓存
请求缓存的原理是第一次请求的时候会根据当前请求的缓存标识把响应输出的内容缓存起来并且设置HTTP缓存(如果判断已经存在请求缓存的话会直接读取请求缓存并且设置HTTP缓存),当第二次访问相同的请求标识的时候,会自动读取HTTP缓存(也就是浏览器缓存)内容而不是真实的调用请求方法,也就是说请求缓存是HTTP缓存+响应(数据)缓存的合体。
如果你需求全局使用请求缓存的话,在应用配置中设置下面的两个配置参数:
~~~
'request_cache' => true,
'request_cache_expire' => 600,
~~~
上面的设置会开启全局请求缓存,默认的缓存标识为当前请求的URL地址(做md5编码处理),并且缓存有效期为600秒,也就是说10分钟之内的相同get请求(请求缓存只支持GET请求)会进行缓存,可以有效提升性能。
我们使用hello方法进行测试:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
return '当前请求时间:' . date('y-m-d H:i:s', request()->time());
}
}
~~~
第一次访问:`http://tp5.com/index/index/hello`
查看请求信息显示:
![](https://box.kancloud.cn/28654fee97af27374916734cbab8336b_791x935.png)
再次访问的时候显示(注意页面输出的请求时间并没有变化):
![](https://box.kancloud.cn/09fca02303002062b453f833da3fc63f_843x925.png)
当给当前URL地址增加参数后,再次访问后会发现请求缓存已经无效了(因为全局请求缓存默认是根据URL地址缓存)。
![](https://box.kancloud.cn/4cc36cbd28b179801f4b8afea8820a35_926x934.png)
如果需要设置个别请求的缓存参数,可以在路由规则中设置,例如:
~~~
// 定义路由规则 并设置3600秒的缓存
Route::get('blog/:id','index/Blog/read',['cache'=>3600]);
路由的请求缓存默认标识是当前的pathinfo地址,也支持指定缓存标识,例如:
// 定义路由规则 并设置3600秒的缓存
Route::get('blog/:id','index/Blog/read',['cache'=>['blog/:id/:page',3600]]);
~~~
缓存标识中的`:id` 和 `:page` 都会被替换成当前请求变量的值。
路由规则中的缓存参数是优先的,并且不依赖全局请求缓存设置,如果你只需要部分请求使用请求缓存功能,那么可以直接在路由规则中设置。
#### 参数绑定
虽然提供了助手函数,在操作方法中读取请求变量的方式还是比较麻烦,使用参数绑定可以进一步简化请求变量的读取,我们通过hello方法的代码来对比下前后两种请求变量的获取方式。
不使用参数绑定的方式:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello()
{
// 获取当前的请求变量
return request()->param('name');
}
}
~~~
使用参数绑定的方式:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello($name = '')
{
// 获取当前请求的name变量
return $name;
}
}
~~~
显而易见使用参数绑定方式的代码更简洁,我们给hello方法添加了name参数(注意:这里给了默认值可以避免当前请求没有传入name变量的时候出错),会自动绑定当前请求变量中的`name变量`(并且支持全局的变量过滤),这种方式就称之为参数绑定。
> 默认情况下,操作方法的参数定义顺序对参数绑定的参数没有影响,参数绑定只是根据参数名自动绑定。
如果我们定义了多个参数:
~~~
<?php
namespace app\index\controller;
class Index
{
public function hello($name = '', $id = 0)
{
return $name . ':' . $id;
}
}
~~~
下面的URL访问都是有效的
~~~
http://tp5.com/index/index/hello/name/thinkphp/id/10
http://tp5.com/index/index/hello/id/10/name/thinkphp
~~~
如果设置了参数顺序绑定的话(所有URL相关的配置必须在应用配置文件中设置):
~~~
// URL参数按顺序解析
'url_param_type' => 1,
~~~
当我们访问下面的URL地址:`http://tp5.com/index/index/hello/thinkphp/10`
页面输出结果为:
> thinkphp:10
当访问下面的URL地址:`http://tp5.com/index/index/hello/10/thinkphp`
页面输出结果为:
> 10:thinkphp
记得始终给参数定义一个默认值可以避免当前请求没有传值的时候抛出异常。
* * * * *
参考:
https://www.kancloud.cn/thinkphp/controller-in-detail/250871