ThinkPHP8学习篇(四):请求和响应
在请求流程中,请求与响应构成了应用与客户端交互的核心纽带 —— 请求负责捕获并解析客户端传递的各类数据(如参数、头信息、请求方式等),响应则承担着将处理结果以合适形式(如页面、JSON、文件等)返回给客户端的重任,是完成一次完整交互的关键闭环。本篇文章将记录 ThinkPHP 请求与响应的学习过程。
一、请求
1、请求对象
当前的请求对象由 think\Request 类负责,该类不需要单独实例化调用,通常使用依赖注入即可。在其它场合则可以使用 think\facade\Request 静态类操作。
在项目里面应该使用 app\Request 对象,该对象继承了系统的 think\Request 对象,但可以增加自定义方法或者覆盖已有方法。
继承 app\BaseController 基础控制器类可以直接使用 request 属性调用 think\Request 对象实例。
当然,继承基础控制器类并不是必须的,接下来说明不继承基础控制器类如何使用请求对象。
1.1、构造方法注入
只需要在构造方法的参数中声明 Request 对象参数就可以在构造方法中使用 Request 对象了。例如:
<?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
namespace app\controller;use think\Request;class Index
{public function index(Request $request){return $request->param('name');}
}
无论是否继承系统的控制器基类,都可以使用操作方法注入。
1.3、静态调用
除了使用依赖注入的方式使用 Request,也可以通过 Facade 机制来静态调用请求对象的方法(注意 use 引入的类库区别)。
示例
<?php
namespace app\controller;use think\facade\Request;class Index
{public function index(){return Request::param('name');}
}
该方法也同样适用于依赖注入无法使用的场合。
1.4、助手函数
为了简化调用,系统还提供了 request 助手函数,可以在任何需要的时候直接调用当前请求对象。
示例
<?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 参数,表示获取带域名的完整地址,例如:
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 | 当前请求的操作名 |
示例
// 获取当前控制器,返回的是控制器的驼峰形式(首字母大写),和控制器类名保持一致(不含后缀)。
Request::controller();
// 获取当前操作,返回的是当前操作方法的实际名称。
Request::action();
3、输入变量
可以通过 Request 对象完成全局输入变量的检测、获取和安全过滤,包括 $_GET、$_POST、$_REQUEST、$_SERVER、$_SESSION、$_COOKIE、$_ENV 等系统变量,以及文件上传信息。
3.1、检测变量是否设置
has(变量名, 请求类型) 方法来检测一个变量参数是否设置。设置返回 true,未设置返回 false。
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 类的如下方法:
变量类型方法('变量名/变量修饰符', '默认值', '过滤方法');
变量类型方法包括:
方法 | 描述 |
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 类型变量是框架提供的用于自动识别当前请求的一种变量获取方式,是系统推荐的获取请求参数的方法,用法如下:
// 获取当前请求的 name 变量
Request::param('name');
// 获取当前请求的所有变量(经过过滤)
Request::param();
// 获取当前请求未经过滤的所有变量
Request::param(false);
// 获取部分变量
Request::param(['name', 'email']);
param 方法会把当前请求类型的参数和 GET 请求合并。
其它的输入变量获取方法和 param 方法用法基本一致。
3.3、变量修饰符
支持对变量使用修饰符功能,可以一定程度上简单过滤变量。
Request::变量类型('变量名/修饰符');
支持的变量修饰符有:
修饰符 | 作用 |
s | 强制转换为字符串类型 |
d | 强制转换为整型类型 |
b | 强制转换为布尔类型 |
a | 强制转换为数组类型 |
f | 强制转换为浮点类型 |
示例
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 变量,传入需要伪装的请求类型,例如:
<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请求头信息。
示例
// 获取的信息为数组,通过信息头名称获取值
$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
namespace app\controller;class Blog
{public function read($year, $month='01'){return 'year=' . $year . '&month=' . $month;}
}
URL的访问地址是:
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
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 方法用于设置响应数据。
示例
response()->data($data);
json()->data($data);
需要注意的是 data 方法设置的只是原始数据,并不一定是最终的输出数据,最终的响应输出数据是会根据当前的 Response 响应类型做自动转换。例如:
json()->data($data);
最终的输出数据就是 json_encode($data) 转换后的数据。
2.2、设置状态码
Response 基类提供的 code 方法用于设置响应的状态码,但一般情况下,都是在调用助手函数的时候直接传入状态码。
示例
json($data, 201);
view($data, 401);// 或者在后面链式调用code方法:
json($data)->code(201);
除了 redirect 函数的默认返回状态码是302之外,其它方法没有指定状态码都是返回200状态码。
2.3、设置头信息
Response 基类提供的 header 方法用于设置响应的头信息。
示例
json($data)->code(201)->header(['Cache-control' => 'no-cache,must-revalidate'
]);
3、重定向
使用 redirect(URL) 助手函数进行重定向。
示例
<?php
namespace app\controller;class Index
{public function hello(){return redirect('http://www.baidu.com');}
}
还可以支持使用 with 方法附加 Session 闪存数据重定向。
<?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(要下载的文件, 显示的文件名, 是否为内容, 有效期(秒)) 用于文件下载。
示例
public function download($year, $month='01')
{return download('image.jpg', 'my.jpg');
}
访问 download 操作就会下载命名为 my.jpg 的图像文件。
下载文件的路径是服务器路径而不是URL路径,如果要下载的文件不存在,系统会抛出异常。
支持设置是否强制下载,例如需要打开图像文件而不是浏览器下载的话,可以使用 force 方法:
public function download()
{// force()方法:是否强制下载return download('image.jpg', 'my.jpg')->force(false);
}
除了 force 方法外,还支持下面的方法:
方法 | 描述 |
name | 命名下载文件 |
expire | 下载有效期 |
isContent | 是否为内容下载 |
mimeType | 设置文件的mimeType类型 |
force | 是否强制下载 |