通过 PHP 代码发送 HTTP 响应和文件下载
1、概述
一个完整的 HTTP 响应报文包含状态行、响应头和响应实体,关于 HTTP 响应底层结构你可以参考 HTTP 报文简介及组成结构这篇教程
https://xueyuanjun.com/post/21121
了解明细,这里不再重复介绍了。
在 PHP 中可以通过内置的 header 函数设置状态行及响应头,而对于响应实体,也就是我们通常看到的 API 响应数据或者 Web 页面响应视图(HTML 文档),通过 PHP 的打印函数输出即可,比如 echo
、printf
、var_dump
等,如果 HTML 和 PHP 脚本混合在一起,则也会解析其中的 PHP 代码,然后渲染对应的 HTML 文档作为响应实体。
耳听为虚,眼见为实,下面学院君结合常见的使用场景来演示如何在 PHP 中设置 HTTP 响应并发送给客户端。
2、响应状态码
我们在 http
目录下新建一个 response.php
来保存本篇教程编写的代码。默认情况下,PHP 返回的响应状态码是 200:
比如我们只通过 echo
设置响应实体,然后在浏览器中访问 http://localhost:9000/response.php
访问这个脚本,在 Chrome 扩展台中可以看到响应状态码正是 200,这是 PHP 底层自动设置的:
我们也可以显式在代码中设置状态码:
<?php
header('HTTP/1.1 200 OK');
echo '你好,学院君';
效果完全一致,响应状态行分三部分,第一部分是 HTTP 协议版本,第二部分是状态码,第三部分是描述状态码的短语。
除了 200 之外,还有很多其他响应状态码,比如 301、403、404、500 等,分别表征不同的含义,比如 301 表示永久重定向、403 表示没有权限、404 表示资源不存在、500 表示服务器错误。
比如说,我们设置一个 404 响应如下:
对应的响应状态行字符串格式需要和 HTTP 协议规范保持一致。合理的使用响应状态码可以对响应状态进行准确的描述,尤其是在 API 接口设计时,调用者根据响应状态码就可以大致得知错误原因。
3、重定向
在 PHP 中,可以通过设置 Location
响应头对用户请求进行重定向:
此时当我们访问 http://localhost:9000/response.php
时,页面会重定向到 https://xueyuanjun.com
:
默认情况下状态码是 302,表示临时重定向,你也可以显示设置这个状态码:
header('HTTP/1.1 302 Found');
header('Location: https://xueyuanjun.com');
还有一个表示永久重定向的状态码 301,要设置 301 重定向,可以这样设置:
header('HTTP/1.1 301 Move Permanently');
header('Location: https://xueyuanjun.com');
重新在浏览器访问该脚本,可以发现重定向状态码已经变成 301 了:
4、HTTP 基本认证
如果某个页面需要经过 HTTP 基本认证才能访问,可以通过设置 WWW-Authenticate
响应头来告知客户端请求用户:
此时访问 http://localhost:9000/response.php
,就会弹出认证表单输入框:
对于这种 HTTP 基本认证中提交的用户名和密码,PHP 默认已经将它们封装到超全局变量 $_SERVER
的 PHP_AUTH_USER
和 PHP_AUTH_PW
字段中(HTTP 协议默认会通过请求头 Authorization
提交这些信息到服务端,关于相关的底层原理可以阅读 HTTP 认证实现方案介绍这篇教程)。
我们在服务端编写对应的处理代码:
// HTTP Basic 认证简单实现
if (empty($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic');
} else {
$name = $_SERVER['PHP_AUTH_USER'];
$pass = $_SERVER['PHP_AUTH_PW'];
if ($name == '学院君' && $pass == '123456') {
echo '用户认证成功,可以访问该页面';
} else {
header('HTTP/1.1 401 Unauthorized');
echo '用户认证失败,请刷新页面重试';
}
}
如果用户提交的用户名或密码不正确,则返回 401 Unauthorized 状态码:
刷新页面重试,如果认证成功,则返回如下提示信息:
你可以在请求头中看到经过 Base64 编码加密的包含用户名和密码字段的 Authorization
字段(Basic 表示基本认证,还有 Digest 表示摘要认证,更安全一些):
不过这种级别的认证等同于明文传输密码了,所以实际项目中不建议通过使用这种认证方案。
关于 PHP 设置 HTTP 响应头学院君就简单介绍到这里,已经覆盖了日常我们经常使用到的场景,当然,还有一块就是 HTTP 缓存的设置,这是一个比较宏大的话题,之前已经在 HTTP 协议详解相关教程中详细介绍过了,除非你想要系统了解 HTTP 缓存实现和性能优化,否则对初学者来说,平时也不太会用到,这里不再单独介绍,我们接下来看看响应实体设置部分。
5、JSON 响应
关于 Web 页面的响应实体输出(主要是 HTML 文档,或者一些调试信息输出,包括文本字符串、数组等),已经都看到过相关的演示实例了,这里我们介绍两种其他的响应输出格式,首先来看 JSON 响应。
在 API 接口中,通常返回的是 JSON 格式数据,JSON 本质上也就是对象字符串,所以在请求处理代码的最后,通过 echo
输出对应的 JSON 对象字符串即可,在 PHP 中,可以通过 PHP 内置的 json_encode 函数快速将对象、数组等格式数据转化为对应的 JSON 字符串。
我们在 http/response.php
中注释掉之前的代码,新增如下 JSON 响应代码:
// JSON 响应
$album = new stdClass();
$album->title = 'PHP 全栈工程师指南';
$album->summary = '基于 Laravel + Vue.js 框架的学习和实战,快速成为合格的 PHP 全栈开发工程师';
$album->author = '学院君';
$album->posts = [
[
'id' => 1,
'title' => 'PHP 入门指南'
],
[
'id' => 2,
'title' => 'Laravel 入门指南'
]
];
echo json_encode($album);
在浏览器中访问 http://localhost:9000/response.php
,返回的 JSON 格式响应数据如下(通过 Chrome 插件 FeHelper 对 JSON 数据渲染进行了优化,这样看起来更加美观):
非常方便。
6、文件下载
接下来,我们来看原生 PHP 代码中如何通过 HTTP 响应实现文件下载。其实也很简单,通过设置相关响应头,然后再通过内置的 readfile 函数读取二进制文件流通过网络输出给客户端浏览器即可。
注释掉 response.php
中的所有代码,新增如下文件下载代码:
// 文件下载
// 设置下载文件内容格式
header('Content-type: application/octet-stream');
// 设置下载文件名
header('Content-Disposition: attachment; filename="laravel.zip"');
// 读取二进制文件流返回给客户端浏览器
$filepath = __DIR__ . '/files/laravel7.zip';
readfile($filepath);
这里我们下载一个位于 Web 根目录下 files
子目录下的 laravel7.zip
文件:
zip 格式文件对应的 MIME 类型是 application/octet-stream
(映射关系可以在这里查询:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types),我们通过 Content-Type
响应头设置即可,然后通过 Content-Disposition
设置下载到本地对应的文件名,最后读取二进制文件流返回给客户端。
在浏览器中访问 http://localhost:9000/response.php
,会弹出一个下载会话框:
点击右下角「存储」按钮保存,即可开始下载该文件。Windows 下也是类似:
7、小结
关于 HTTP 服务器、请求和响应部分我们就简单介绍到这里,由于 HTTP 协议本身是无状态的,而在某些场景中我们希望 HTTP 请求能够「记住」用户状态,比如实现用户认证、记住记录登录状态、电商网站中加入购物车到下单支付,这些都涉及到多次请求,多个页面,但是我们希望 HTTP 请求能够识别来自同一个用户的不同请求,为此,又引入了 Cookie 和 Session 的概念。下篇教程,我们将一起来探索 Cookie 和 Session,以及基于它们实现更加安全的用户认证解决方案(相对于前面的 HTTP 基本认证)。
推荐阅读
-
IOS UI 自动化测试实践:pyhton-wda 环境设置篇-Xcode 版本:10.1iphone 版本:12.0.1OS 版本:10.13.6 实践开始 创建一个新目录并从 git 下载 WDA 项目代码。 git clone https://github.com/facebook/WebDriverAgent 并运行初始化脚本。 ./Scripts/bootstrap.sh 出现以下错误信息:原因:Carthage 需要下载相关的依赖项,而这些依赖项并未在本地安装。 解决方法通过 brew 下载并安装依赖项: brew install carthage 下载成功并初始化脚本后,出现以下错误:原因:需要 npm 来打包响应 js 文件,而机器上未安装该文件。 解决方案:通过 brew 下载并安装 npm(注:brew 真的是个好东西):brew install npm 安装成功后,继续初始化脚本。/Scripts/bootstrap.sh Xcode 相关操作
-
通过 PHP 代码发送 HTTP 响应和文件下载
-
通过 HTTP 上传和下载 C/C++ 文件
-
通过 HTTP 上传和下载 C/C++ 文件
-
Java 类加载器的作用 - 简介:类加载器是 Java™ 中一个非常重要的概念。类加载器负责将 Java 类的字节码加载到 Java 虚拟机中。本文首先详细介绍了 Java 类加载器的基本概念,包括代理模型、加载类的具体过程和线程上下文类加载器等。然后介绍了如何开发自己的类加载器,最后介绍了类加载器在 Web 容器和 OSGi™ 中的应用。 类加载器是 Java 语言的一项创新,也是 Java 语言广受欢迎的重要原因之一。它允许将 Java 类动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 开始出现,最初是为了满足 Java Applets 的需求而开发的,Java Applets 需要从远程位置下载 Java 类文件并在浏览器中执行。现在,类加载器已广泛应用于网络容器和 OSGi。一般来说,Java 应用程序的开发人员不需要直接与类加载器交互;Java 虚拟机的默认行为足以应对大多数情况。但是,如果遇到需要与类加载器交互的情况,而您又不太了解类加载器的机制,就很容易花费大量时间调试异常,如 ClassNotFoundException 和 NoClassDefFoundError。本文将详细介绍 Java 的类加载器,帮助读者深入理解 Java 语言中的这一重要概念。下面先介绍一些基本概念。 类加载器的基本概念 顾名思义,类加载器用于将 Java 类加载到 Java 虚拟机中。一般来说,Java 虚拟机以如下方式使用 Java 类:Java 源程序(.java 文件)经 Java 编译器编译后转换为 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码并将其转换为 java.lang 实例。每个实例都用来表示一个 Java 类。通过该实例的 newInstance 方法创建该类的对象。实际情况可能更加复杂,例如,Java 字节代码可能是由工具动态生成或通过网络下载的。 基本上,所有类加载器都是 java.lang.ClassLoader 类的实例。下面将详细介绍这个 Java 类。 java.lang.ClassLoader 类简介 java.lang.ClassLoader 类的基本职责是根据给定类的名称为其查找或生成相应的字节码,然后根据这些字节码定义一个 Java 类,即 java.lang.Class 类的实例。除此之外,ClassLoader 还负责加载 Java 应用程序所需的资源,如图像文件和配置文件。不过,本文只讨论它加载类的功能。为了履行加载类的职责,ClassLoader 提供了许多方法,其中比较重要的方法如表 1 所示。下文将详细介绍这些方法。 表 1.与加载类相关的 ClassLoader 方法
-
南邮OJ Web任务大揭秘:层层挑战剖析 1. 挑战一:迷宫般的目录探索 题目作者似乎穷举了所有可能的目录组合,最终在404.php中的