Web 应用程序与 HTTP
学习HTTP协议的工作原理以及HTTP消息的主要组成部分。
什么是Web应用?
既然我们要开发Web应用,那首先得弄清楚这个术语的含义。Web应用是一种运行在服务器上的程序,通过HTTP协议在网络中访问,通常在浏览器中打开。对我们而言,这是一个宽泛的概念,涵盖了网站、SaaS(软件即服务)应用、社交网络,甚至移动应用的后端系统。这个定义之所以全面,是因为作为开发者,我们构建这些系统的方式非常相似。
什么是HTTP?
超文本传输协议(HTTP) 是一种用于超文本的应用层协议,也就是我们所熟知的互联网。它是一种无状态协议,设计为人类可读且可扩展。
超文本 是带有超链接的文本,可以指向其他超文本文档。我们也可以用它来表示格式化文本以及不同类型的资源,比如图片。
下面是一个本课中展示的超文本工作示例。观察浏览器地址栏中的内容,会看到一个页面的URL,例如 https://www.example.com/courses/developing-web-applications-with-php/web-applications-and-http。当浏览器尝试打开该页面时,它使用 HTTPS(安全HTTP)协议向 www.example.com 后面的服务器发送一个 GET /courses/developing-web-applications-with-php/web-applications-and-http 请求。服务器返回一个包含对其他文件(如图片、样式表和客户端脚本)引用的文档,浏览器则通过单独的HTTP请求加载这些资源。所有请求的响应最终组合成完整的页面。
HTTP消息长什么样
HTTP请求和响应都由两大部分组成:头部和主体,两者之间以一个空行分隔。
请求的结构如下:
- 起始行,包含HTTP方法、请求路径和协议版本——例如
GET /get?foo=bar HTTP/1.1。 - 请求头,每行一个,格式为
名称: 值——例如Host: httpbin.org。 - 一个空行,表示头部部分结束。
- 可选的请求体,可能是二进制数据。
响应的结构如下:
- 起始行,包含协议版本、状态码和状态描述——例如
HTTP/1.1 200 OK。 - 响应头,每行一个,格式为
名称: 值——例如Content-Type: application/json。 - 一个空行,表示头部部分结束。
- 可选的响应体,也可能是二进制数据。
为了直观理解,我们来向 httpbin.org 发起一次真实请求,这是一个用于HTTP调试的服务。我们将使用 curl 作为命令行HTTP客户端。命令如下:
1curl -v 'http://httpbin.org/get?foo=bar'
-v 标志指示 curl 输出调试信息,参数是请求的URL。在输出中,请求头以 > 开头,响应头以 < 开头。
URL
HTTP中最关键的概念可能是统一资源定位符(URL)。它代表了全球资源的地址。浏览器地址栏中的字符串就是一个URL。
让我们具体分析URL的各个组成部分。
协议方案 告诉客户端使用哪种协议。目前我们只关注 http 和 https。
主机名 指定要请求资源的服务器。我们可以使用服务器的IP地址作为主机名,但使用域名更实用。当主机名是域名时,客户端首先进行DNS查询以获取服务器的IP地址,然后通过IP地址发起请求。
端口号 是客户端需要连接的服务器端口编号。该字段是可选的,因为HTTP有标准端口(80),HTTPS也有标准端口(443)。但如果我们的Web服务器运行在非标准端口上,则必须在URL中指定端口号。
主机名和端口号合起来称为权威信息。
路径 指向服务器上的某个特定文件。但在PHP应用处理请求时,路径背后通常并不存在实际文件。相反,PHP应用将路径作为输入,决定返回哪个实体。通常,Web服务器采用混合配置:如果路径指向一个真实存在的文件,则直接返回;否则,交由PHP处理请求。
查询参数 是以 ? 开头的键值对集合。由于其标准化格式(key=value,多个参数用 & 分隔),解析和修改都非常方便。服务器将这些参数作为输入使用。还可以通过指定参数名传递数组或嵌套结构,例如 foo[] 或 foo[bar]。
锚点 是可选的部分,用于指向页面中的特定元素。以 # 开头。浏览器不会将锚点发送给服务器,而是在页面加载完成后用于滚动到该元素位置。
根路径相对URL
我们并不总是需要为内部链接指定主机名。如果链接省略了协议和权威信息,且以文件路径(以 / 开头的部分)开始,客户端会复用当前页面的协议和权威信息。
还有其他形式的相对URL,但这些用法不推荐。
HTTP方法
我们示例中的HTTP请求以 GET 开头。这就是一个HTTP方法。它告诉服务器我们希望对资源执行什么操作。最常见的两个HTTP方法是 GET(用于从服务器获取数据)和 POST(用于向服务器发送数据)。
接下来,我们通过 curl 向 httpbin.org 发送一个 POST 请求,看看它的样子。命令与之前类似,但现在我们使用 -F 标志在请求体中添加一些键值数据。
1curl -v 'http://httpbin.org/post' -F 'key1=value1' -F 'key2=value2'
现在HTTP方法是 POST,请求的内容类型是 multipart/form-data。我们看不到表单数据,因为它位于请求体中,而 curl 没有记录这部分内容。但从响应中可以看出,服务器确实接收到了请求数据。
还有更多HTTP方法可用于不同场景。我们将在后续关于REST API的课程中深入学习。
HTTP响应状态码
每个HTTP响应的第一行包含响应码和三位数字,后跟状态码名称。这告诉我们服务器如何处理请求。HTTP规范中定义了一组固定的常用错误码。
状态码分为五类,每类以不同的首位数字区分:
- 信息性响应(100–199):由Web服务器使用。
- 成功响应(200–299):表示服务器成功处理了请求。
- 重定向消息(300–399):用于将客户端重定向到另一个URL。
- 客户端错误响应(400–499):表示客户端导致了错误。
- 服务器错误响应(500–599):表示服务器自身出现了问题。
最常用的响应状态码包括:
- 200 OK:成功请求的默认状态码。
- 303 See Other:将客户端重定向到另一个URL,使用GET请求。
- 400 Bad Request:请求语法无效时使用。
- 401 Unauthorized:表示客户端需要认证才能访问该资源。
- 403 Forbidden:客户端已认证,但无权访问该资源。
- 404 Not Found:服务器找不到请求的资源。这是互联网上最著名的状态码。
- 500 Internal Server Error:用于通知发生未预期的服务器端错误。
常见的HTTP头部
标准HTTP头部有很多,也可以使用任意非标准头部。因此无法记住全部。但有些头部组使用频率很高,建议熟悉它们。
Content-Type头部 包含消息主体的MIME类型,让浏览器能以不同方式处理内容。
Cookie头部 用于在请求之间保持会话。Set-Cookie 头部会指示浏览器保存一个值,并在后续每次请求中自动发送,直到该Cookie过期。虽然HTTP本身是无状态的,但Cookie是识别同一用户多次请求的关键机制。
Authorization头部 用于在HTTP层面管理客户端认证。尽管用户通常通过Cookie认证,但通过头部认证在处理API请求时非常有用。
缓存头部 通过在浏览器或代理服务器上缓存响应来减少生成响应的工作量。服务器可以发送缓存指令,客户端也可以发送指令,例如“如果缓存内容不匹配,则提供新响应”。
什么是HTTPS?
HTTP以明文形式在网络上传输消息的想法并不安全,因为客户端与服务器之间的连接并非直接。互联网上的消息需经过多个路由服务器,其中一些可能具有恶意行为。每个中间服务器都可能实施中间人攻击,窃听或篡改传输中的消息。例如,黑客可以搭建虚假Wi-Fi网络,窃取用户的密码和信用卡信息。
为解决这些问题,我们使用HTTP的一个扩展——超文本传输安全协议(HTTPS)。它对消息进行加密,确保客户端与服务器之间的通信无法被第三方读取。同时,它使用数字证书,使服务器能够证明消息确实来自真实的服务器,而非恶意伪造者。
只要可能,我们都应使用HTTPS。
HTTPS对我们有何影响
我们刚刚只是触及了HTTPS的皮毛,还有很多内容值得深入学习。但好消息是,我们不需要把所有细节都记在脑子里。通常情况下,我们只需生成响应体,其余的HTTP通信由Web服务器自动完成。偶尔遇到问题时,可能需要调整某些头部。
我们经常使用的两个方面是请求方法和响应状态码。请务必熟悉它们,因为它们在后续开发中将非常有用。
总结
Web应用的显著特征是通过HTTP协议处理请求。HTTP是一种传输超文本和多种内容类型的数据协议。
HTTP消息基于文本,人类可读,但消息体可能是二进制的。每条消息以起始行开头,接着是HTTP头部的键值映射,一个空行,最后是消息体。在请求中,起始行包含HTTP方法和路径;在响应中,包含状态码。
我们必须理解HTTP,因为它是我们PHP应用与外部世界交互的主要接口。