在请求流程中,请求与响应构成了应用与客户端交互的核心纽带 ------ 请求负责捕获并解析客户端传递的各类数据(如参数、头信息、请求方式等),响应则承担着将处理结果以合适形式(如页面、JSON、文件等)返回给客户端的重任,是完成一次完整交互的关键闭环。本篇文章将记录 ThinkPHP 请求与响应的学习过程。
一、请求
1、请求对象
当前的请求对象由 think\Request 类负责,该类不需要单独实例化调用,通常使用依赖注入即可。在其它场合则可以使用 think\facade\Request 静态类操作。
在项目里面应该使用 app\Request 对象,该对象继承了系统的 think\Request 对象,但可以增加自定义方法或者覆盖已有方法。
继承 app\BaseController 基础控制器类可以直接使用 request 属性调用 think\Request 对象实例。
当然,继承基础控制器类并不是必须的,接下来说明不继承基础控制器类如何使用请求对象。
1.1、构造方法注入
只需要在构造方法的参数中声明 Request 对象参数就可以在构造方法中使用 Request 对象了。例如:
php
<?php
namespace app\controller;
use think\Request;
class Index
{
// 在类中声明 Request 实例
protected $request;
/**
* 构造方法,通过向构造方法中传入 Request 对象完成注入
* @param Request $request
*/
public function __construct(Request $request)
{
// 将注入的 Request 对象赋值给当前类中的 $request
$this->request = $request;
}
public function index()
{
return $this->request->param('name');
}
}
1.2、操作方法注入
Request 对象可以通过构造方法注入,同样的,也可以通过普通方法进行注入。注入的方式是一样的,只需要在需要注入 Request 对象的方法上声明该参数就可以了。例如:
php
<?php
namespace app\controller;
use think\Request;
class Index
{
public function index(Request $request)
{
return $request->param('name');
}
}
无论是否继承系统的控制器基类,都可以使用操作方法注入。
1.3、静态调用
除了使用依赖注入的方式使用 Request,也可以通过 Facade 机制来静态调用请求对象的方法(注意 use 引入的类库区别)。
示例
php
<?php
namespace app\controller;
use think\facade\Request;
class Index
{
public function index()
{
return Request::param('name');
}
}
该方法也同样适用于依赖注入无法使用的场合。
1.4、助手函数
为了简化调用,系统还提供了 request 助手函数,可以在任何需要的时候直接调用当前请求对象。
示例
php
<?php
namespace app\controller;
class Index
{
public function index()
{
return request()->param('name');
}
}
1.5、自定义请求对象
可以在项目里面自定义 Request 对象,修改已有的方法或者增加新的方法,默认已经在项目里准备了 app\Request 类,只需要直接修改该类就可以为你的项目单独自定义请求对象。
2、请求信息
Request 对象支持获取当前的请求信息,包括:
|-------------|----------------------------|
| 方法 | 含义 |
| host | 当前访问域名或者IP |
| scheme | 当前访问协议 |
| port | 当前访问的端口 |
| remotePort | 当前请求的REMOTE_PORT |
| protocol | 当前请求的SERVER_PROTOCOL |
| contentType | 当前请求的CONTENT_TYPE |
| domain | 当前包含协议的域名 |
| subDomain | 当前访问的子域名 |
| panDomain | 当前访问的泛域名 |
| rootDomain | 当前访问的根域名 |
| url | 当前完整URL |
| baseUrl | 当前URL(不含QUERY_STRING) |
| query | 当前请求的QUERY_STRING参数 |
| baseFile | 当前执行的文件 |
| root | URL访问根地址 |
| rootUrl | URL访问根目录 |
| pathinfo | 当前请求URL的pathinfo信息(含URL后缀) |
| ext | 当前URL的访问后缀 |
| time | 获取当前请求的时间 |
| type | 当前请求的资源类型 |
| method | 当前请求类型 |
| rule | 当前请求的路由对象实例 |
对于上面的这些请求方法,在调用时无需任何参数,但某些方法可以传入 true 参数,表示获取带域名的完整地址,例如:
php
use think\facade\Request;
// 获取完整URL地址 不带域名
Request::url();
// 获取完整URL地址 包含域名
Request::url(true);
// 获取当前URL(不含QUERY_STRING) 不带域名
Request::baseFile();
// 获取当前URL(不含QUERY_STRING) 包含域名
Request::baseFile(true);
// 获取URL访问根地址 不带域名
Request::root();
// 获取URL访问根地址 包含域名
Request::root(true);
2.1、获取当前控制器/操作
可以通过请求对象获取当前请求的控制器/操作名。
|------------|-----------|
| 方法 | 含义 |
| controller | 当前请求的控制器名 |
| action | 当前请求的操作名 |
示例
php
// 获取当前控制器,返回的是控制器的驼峰形式(首字母大写),和控制器类名保持一致(不含后缀)。
Request::controller();
// 获取当前操作,返回的是当前操作方法的实际名称。
Request::action();
3、输入变量
可以通过 Request 对象完成全局输入变量的检测、获取和安全过滤,包括 _GET、_POST、_REQUEST、_SERVER、_SESSION、_COOKIE、$_ENV 等系统变量,以及文件上传信息。
3.1、检测变量是否设置
has(变量名, 请求类型) 方法来检测一个变量参数是否设置。设置返回 true,未设置返回 false。
php
Request::has('id','get'); // GET 请求中是否设置 id 变量
Request::has('name','post'); // POST 请求中是否设置 name 变量
变量检测可以支持所有支持的系统变量,包括:get/post/put/request/cookie/server/session/env/file。
3.2、变量获取
变量获取使用 \think\Request 类的如下方法:
php
变量类型方法('变量名/变量修饰符', '默认值', '过滤方法');
变量类型方法包括:
|------------|--------------------------------------|
| 方法 | 描述 |
| param | 获取当前请求的变量 |
| get | 获取 _GET 变量 |
| post | 获取 _POST 变量 |
| put | 获取 PUT 变量 |
| delete | 获取 DELETE 变量 |
| session | 获取 SESSION 变量 |
| cookie | 获取 _COOKIE 变量 |
| request | 获取 _REQUEST 变量 |
| server | 获取 _SERVER 变量 |
| env | 获取 _ENV 变量 |
| route | 获取 路由(包括PATHINFO) 变量 |
| middleware | 获取 中间件赋值/传递的变量 |
| file | 获取 _FILES 变量 |
| all | 获取包括 _FILES 变量在内的请求变量,相当于param+file |
PARAM 类型变量是框架提供的用于自动识别当前请求的一种变量获取方式,是系统推荐的获取请求参数的方法,用法如下:
php
// 获取当前请求的 name 变量
Request::param('name');
// 获取当前请求的所有变量(经过过滤)
Request::param();
// 获取当前请求未经过滤的所有变量
Request::param(false);
// 获取部分变量
Request::param(['name', 'email']);
param 方法会把当前请求类型的参数和 GET 请求合并。
其它的输入变量获取方法和 param 方法用法基本一致。
3.3、变量修饰符
支持对变量使用修饰符功能,可以一定程度上简单过滤变量。
php
Request::变量类型('变量名/修饰符');
支持的变量修饰符有:
|---------|------------|
| 修饰符 | 作用 |
| s | 强制转换为字符串类型 |
| d | 强制转换为整型类型 |
| b | 强制转换为布尔类型 |
| a | 强制转换为数组类型 |
| f | 强制转换为浮点类型 |
示例
php
Request::get('id/d'); // 变量 id 为整型
Request::post('name/s'); // 变量 name 为字符串
Request::post('ids/a'); // 变量 ids 为数组
4、请求类型
4.1、获取请求类型
在有些情况下,我们需要判断当前操作的请求类型是GET、POST、PUT、DELETE或者HEAD,一方面可以针对请求类型作出不同的逻辑处理,另外一方面有些情况下面需要验证安全性,过滤不安全的请求。
请求对象 Request 类提供了下列方法来获取或判断当前请求类型:
|---------------|-----------|
| 用途 | 方法 |
| 获取当前请求类型 | method |
| 判断是否GET请求 | isGet |
| 判断是否POST请求 | isPost |
| 判断是否PUT请求 | isPut |
| 判断是否DELETE请求 | isDelete |
| 判断是否AJAX请求 | isAjax |
| 判断是否PJAX请求 | isPjax |
| 判断是否JSON请求 | isJson |
| 判断是否手机访问 | isMobile |
| 判断是否HEAD请求 | isHead |
| 判断是否PATCH请求 | isPatch |
| 判断是否OPTIONS请求 | isOptions |
| 判断是否为CLI执行 | isCli |
| 判断是否为CGI模式 | isCgi |
method 方法返回的请求类型始终是大写,这些方法都不需要传入任何参数。
4.2、请求类型伪装
ThinkPHP 支持请求类型伪装,可以在POST表单里面提交 _method 变量,传入需要伪装的请求类型,例如:
html
<form method="post" action="">
<input type="text" name="name" value="Hello">
<input type="hidden" name="_method" value="PUT" >
<input type="submit" value="提交">
</form>
提交后的请求类型会被系统识别为 PUT 请求。
可以设置为任何合法的请求类型,包括GET、POST、PUT和DELETE等,但伪装变量 _method 只能通过 POST 请求进行提交。
5、HTTP头信息
可以使用 Request 对象的 header 方法获取当前请求的HTTP请求头信息。
示例
php
// 获取的信息为数组,通过信息头名称获取值
$info = Request::header();
echo $info['accept'];
echo $info['accept-encoding'];
echo $info['user-agent'];
// 也可以直接获取某个请求头信息
$agent = Request::header('user-agent');
HTTP 请求头信息的名称不区分大小写,并且 _ 会自动转换为 - 。
6、参数绑定
参数绑定是把当前请求的变量作为操作方法的参数直接传入,参数绑定并不区分请求类型。
参数绑定方式默认是按照变量名进行绑定。例如,我们给 Blog 控制器定义了 read 方法,read 方法需要指定年份(year)和月份(month)两个参数,那么我们可以这样定义:
php
<?php
namespace app\controller;
class Blog
{
public function read($year, $month='01')
{
return 'year=' . $year . '&month=' . $month;
}
}
URL的访问地址是:
php
http://serverName/blog/read/year/2025/month/12
或者
http://serverName/blog/read?year=2021&month=02
按照变量名进行参数绑定的参数必须和URL中传入的变量名称一致,但是参数顺序不需要。
二、响应
ThinkPHP的 Response 响应对象由 think\Response 类或者子类完成,ThinkPHP的响应输出是自动的,最终会调用 Response 对象的 send 方法完成输出。
1、响应输出
大多数情况下,我们不需要关注 Response 对象本身,只需要在控制器的操作方法中返回数据即可。最简单的响应输出是直接在控制器操作方法中返回一个字符串,例如:
php
<?php
namespace app\controller;
class Index
{
public function hello($name='thinkphp')
{
return 'Hello,' . $name . '!';
}
}
默认是输出 html。如果发起一个 JSON 请求的话,输出就会自动使用 JSON 格式响应输出。
为了规范和清晰起见,最佳的方式是在控制器最后明确输出类型。默认支持的输出类型包括:
|----------|----------|-----------------------------|
| 输出类型 | 快捷方法 | 对应Response类 |
| HTML输出 | response | \think\Response |
| 渲染模板输出 | view | \think\response\View |
| JSON输出 | json | \think\response\Json |
| JSONP输出 | jsonp | \think\response\Jsonp |
| XML输出 | xml | \think\response\Xml |
| 页面重定向 | redirect | \think\response\Redirect |
| 附件下载 | download | \think\response\File |
2、响应参数
Response 对象提供了一系列方法用于设置响应参数,包括设置输出内容、状态码及 header 信息等,并且支持链式调用以及多次调用。
2.1、设置数据
Response 基类提供的 data 方法用于设置响应数据。
示例
php
response()->data($data);
json()->data($data);
需要注意的是 data 方法设置的只是原始数据,并不一定是最终的输出数据,最终的响应输出数据是会根据当前的 Response 响应类型做自动转换。例如:
php
json()->data($data);
最终的输出数据就是 json_encode($data) 转换后的数据。
2.2、设置状态码
Response 基类提供的 code 方法用于设置响应的状态码,但一般情况下,都是在调用助手函数的时候直接传入状态码。
示例
php
json($data, 201);
view($data, 401);
// 或者在后面链式调用code方法:
json($data)->code(201);
除了 redirect 函数的默认返回状态码是302之外,其它方法没有指定状态码都是返回200状态码。
2.3、设置头信息
Response 基类提供的 header 方法用于设置响应的头信息。
示例
php
json($data)->code(201)->header([
'Cache-control' => 'no-cache,must-revalidate'
]);
3、重定向
使用 redirect(URL) 助手函数进行重定向。
示例
php
<?php
namespace app\controller;
class Index
{
public function hello()
{
return redirect('http://www.baidu.com');
}
}
还可以支持使用 with 方法附加 Session 闪存数据重定向。
php
<?php
namespace app\controller;
class Index
{
public function index()
{
return redirect('/hello')->with('name','thinkphp');
}
public function hello()
{
$name = session('name');
return 'hello,'.$name.'!';
}
}
需要说明的是,Session 闪存的数据仅在下一次请求有效,再次访问重定向地址的时候无效。
4、文件下载
支持文件下载功能,可以更简单的读取文件进行下载操作,支持直接下载输出内容。
助手函数 download(要下载的文件, 显示的文件名, 是否为内容, 有效期(秒)) 用于文件下载。
示例
php
public function download($year, $month='01')
{
return download('image.jpg', 'my.jpg');
}
访问 download 操作就会下载命名为 my.jpg 的图像文件。
下载文件的路径是服务器路径而不是URL路径,如果要下载的文件不存在,系统会抛出异常。
支持设置是否强制下载,例如需要打开图像文件而不是浏览器下载的话,可以使用 force 方法:
php
public function download()
{
// force()方法:是否强制下载
return download('image.jpg', 'my.jpg')->force(false);
}
除了 force 方法外,还支持下面的方法:
|-----------|-----------------|
| 方法 | 描述 |
| name | 命名下载文件 |
| expire | 下载有效期 |
| isContent | 是否为内容下载 |
| mimeType | 设置文件的mimeType类型 |
| force | 是否强制下载 |