Symfony 和 HTTP 基础知识
好消息!在学习 Symfony 的同时,你也在学习 Web 的基础知识。Symfony 紧密地模仿 HTTP 请求-响应流程:这是几乎所有 Web 通信背后的基本范例。
在本文中,你将了解 HTTP 基础知识,并了解这些知识如何在 Symfony 中应用。
HTTP 中的请求和响应
HTTP (超文本传输协议) 是一种文本语言,允许两台机器相互通信。例如,当查看最新的 xkcd 漫画时,会发生以下(大致的)对话
HTTP 是用来描述这种基于文本的语言的术语。你的服务器的目标始终是理解文本请求并返回文本响应。
Symfony 从一开始就围绕这个现实构建。无论你是否意识到,HTTP 都是你每天都在使用的东西。通过 Symfony,你将学会如何掌握它。
步骤 1:客户端发送请求
Web 上的每次对话都始于一个请求。请求是由客户端(例如浏览器、智能手机应用等)以称为 HTTP 的特殊格式创建的文本消息。客户端将该请求发送到服务器,然后等待响应。
看看浏览器和 xkcd Web 服务器之间交互的第一部分(请求)
用 HTTP 术语来说,这个 HTTP 请求实际上看起来像这样
1 2 3 4
GET / HTTP/1.1
Host: xkcd.com
Accept: text/html
User-Agent: Mozilla/5.0 (Macintosh)
这几行代码传达了关于客户端正在请求的确切资源的所有必要信息。HTTP 请求的第一行是最重要的,因为它包含两个重要的东西:HTTP 方法 (GET) 和 URI (/
)。
URI (例如 /
, /contact
等) 是唯一地址或位置,用于标识客户端想要的资源。HTTP 方法(例如 GET
)定义了客户端想要对资源执行的操作。HTTP 方法(也称为动词)定义了客户端可以对资源执行的几种常见方式 - 最常见的 HTTP 方法是
- GET
- 从服务器检索资源(例如,当访问页面时);
- POST
- 在服务器上创建资源(例如,当提交表单时);
- PUT/PATCH
- 更新服务器上的资源(API 使用);
- DELETE
- 从服务器删除资源(API 使用)。
考虑到这一点,你可以想象一个 HTTP 请求看起来像什么,例如删除一篇特定的博客文章
1
DELETE /blog/15 HTTP/1.1
注意
实际上,HTTP 规范定义了九种 HTTP 方法,但其中许多方法没有被广泛使用或支持。实际上,许多现代浏览器仅在 HTML 表单中支持 POST
和 GET
。然而,各种其他方法在 XMLHttpRequest 中得到支持。
除了第一行之外,HTTP 请求总是包含其他信息行,称为请求头。标头可以提供各种各样的信息,例如被请求资源的主机 (Host
)、客户端接受的响应格式 (Accept
) 以及客户端用于发出请求的应用程序 (User-Agent
)。存在许多其他标头,可以在 Wikipedia 的 HTTP 标头字段列表 文章中找到。
步骤 2:服务器返回响应
一旦服务器收到请求,它就确切地知道客户端需要哪个资源(通过 URI)以及客户端想要对该资源做什么(通过方法)。例如,在 GET 请求的情况下,服务器准备资源并在 HTTP 响应中返回它。考虑来自 xkcd Web 服务器的响应
翻译成 HTTP,发送回浏览器的响应将看起来像这样
1 2 3 4 5 6 7 8
HTTP/1.1 200 OK
Date: Sat, 02 Apr 2011 21:05:05 GMT
Server: lighttpd/1.4.19
Content-Type: text/html
<html>
<!-- ... HTML for the xkcd comic -->
</html>
HTTP 响应包含请求的资源(在本例中为 HTML 内容),以及关于响应的其他信息。第一行尤其重要,它包含 HTTP 响应状态代码(在本例中为 200)。
状态代码将请求的总体结果传达回客户端。请求成功了吗?有错误吗?存在不同的状态代码,指示成功、错误或客户端需要做某事(例如,重定向到另一个页面)。查看 HTTP 状态代码列表。
与请求一样,HTTP 响应包含称为 HTTP 标头的附加信息。同一资源的主体可以以多种不同的格式返回,例如 HTML、XML 或 JSON,并且 Content-Type
标头使用 Internet 媒体类型(如 text/html
)来告诉客户端正在返回哪种格式。你可以从 IANA 查看 常用媒体类型列表。
存在许多其他标头,其中一些非常强大。例如,某些标头可用于创建强大的缓存系统。
请求、响应和 Web 开发
这种请求-响应对话是驱动 Web 上所有通信的基本过程。
最重要的事实是:无论你使用哪种语言、构建哪种类型的应用程序(Web、移动、JSON API)或遵循哪种开发理念,应用程序的最终目标始终是理解每个请求并创建和返回适当的响应。
另请参阅
要了解更多关于 HTTP 规范的信息,请阅读原始的 HTTP 1.1 RFC 或 HTTP Bis,后者是澄清原始规范的积极努力。
PHP 中的请求和响应
那么,当使用 PHP 时,你如何与“请求”交互并创建“响应”呢?实际上,PHP 在一定程度上将你从整个过程中抽象出来
1 2 3 4 5 6
$uri = $_SERVER['REQUEST_URI'];
$foo = $_GET['foo'];
header('Content-Type: text/html');
echo 'The URI requested is: '.$uri;
echo 'The value of the "foo" parameter is: '.$foo;
尽管听起来很奇怪,但这个小型应用程序实际上正在从 HTTP 请求中获取信息,并使用它来创建 HTTP 响应。PHP 没有解析原始 HTTP 请求消息,而是准备了包含来自请求的所有信息的超全局变量(例如 $_SERVER
和 $_GET
)。类似地,你可以使用 PHP header 函数来创建响应头,并打印出将作为响应消息内容部分的实际内容,而不是返回 HTTP 格式的文本响应。PHP 将创建一个真正的 HTTP 响应并将其返回给客户端
1 2 3 4 5 6 7
HTTP/1.1 200 OK
Date: Sat, 03 Apr 2011 02:14:33 GMT
Server: Apache/2.2.17 (Unix)
Content-Type: text/html
The URI requested is: /testing?foo=symfony
The value of the "foo" parameter is: symfony
Symfony 中的请求和响应
Symfony 通过两个类提供了一种替代原始 PHP 方法的方案,这两个类允许你以更简单的方式与 HTTP 请求和响应进行交互。
Symfony 请求对象
Request 类是 HTTP 请求消息的面向对象表示。有了它,你可以轻松获得所有请求信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
// the URI being requested (e.g. /about) minus any query parameters
$request->getPathInfo();
// retrieves $_GET and $_POST variables respectively
$request->query->get('id');
$request->getPayload()->get('category', 'default category');
// retrieves $_SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by "attachment"
$request->files->get('attachment');
// retrieves a $_COOKIE value
$request->cookies->get('PHPSESSID');
// retrieves an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content-type');
$request->getMethod(); // e.g. GET, POST, PUT, DELETE or HEAD
$request->getLanguages(); // an array of languages the client accepts
额外的好处是,Request
类在后台做了很多工作,你永远不需要担心。例如,isSecure()
方法检查 PHP 中可以指示用户是否通过安全连接(即 HTTPS)连接的三个不同值。
Symfony 响应对象
Symfony 还提供了一个 Response 类:HTTP 响应消息的 PHP 表示。这允许你的应用程序使用面向对象的接口来构建需要返回给客户端的响应
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\HttpFoundation\Response;
$response = new Response();
$response->setContent('<html><body><h1>Hello world!</h1></body></html>');
$response->setStatusCode(Response::HTTP_OK);
// sets a HTTP response header
$response->headers->set('Content-Type', 'text/html');
// prints the HTTP headers followed by the content
$response->send();
还有几个响应子类可以帮助你返回 JSON、重定向、流式文件下载 等等。
提示
Request
和 Response
类是一个名为 symfony/http-foundation 的独立组件的一部分,你可以在任何 PHP 项目中使用它。它还包含用于处理会话、文件上传等的类。
即使 Symfony 没有提供其他任何东西,你也将拥有一个用于访问请求信息的工具包和一个用于创建响应的面向对象接口。即使在你学习 Symfony 的许多强大功能时,也要记住,你的应用程序的目标始终是解释请求并根据你的应用程序逻辑创建适当的响应。
从请求到响应的旅程
就像 HTTP 本身一样,使用 Request
和 Response
对象非常简单。构建应用程序的难点在于编写两者之间的代码。换句话说,真正的工作在于编写解释请求信息并创建响应的代码。
你的应用程序可能做了很多事情,例如发送电子邮件、处理表单提交、将内容保存到数据库、渲染 HTML 页面以及使用安全性保护内容。你如何管理所有这些,同时仍然保持代码的组织性和可维护性?Symfony 的创建就是为了帮助你解决这些问题。
前端控制器
传统上,应用程序的构建方式是站点的每个“页面”都是其自己的物理文件(例如 index.php
、contact.php
等)。
这种方法存在几个问题,包括 URL 的不灵活性(如果你想将 blog.php
更改为 news.php
而不破坏所有链接怎么办?)以及每个文件必须手动包含一组核心文件,以便站点的安全性、数据库连接和“外观”可以保持一致。
一个更好的解决方案是使用前端控制器:一个处理进入你的应用程序的每个请求的单个 PHP 文件。例如
/index.php |
执行 index.php |
/index.php/contact |
执行 index.php |
/index.php/blog |
执行 index.php |
提示
通过在你的 Web 服务器配置 中使用重写规则,将不再需要 index.php
,你将拥有美观、简洁的 URL(例如 /show
)。
现在,每个请求都以完全相同的方式处理。前端控制器始终被执行,而不是单个 URL 执行不同的 PHP 文件,并且不同 URL 到你的应用程序不同部分的路由在内部完成。
一个小型前端控制器可能看起来像这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// index.php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$path = $request->getPathInfo(); // the URI path being requested
if (in_array($path, ['', '/'])) {
$response = new Response('Welcome to the homepage.');
} elseif ('/contact' === $path) {
$response = new Response('Contact us');
} else {
$response = new Response('Page not found.', Response::HTTP_NOT_FOUND);
}
$response->send();
这样更好,但这仍然有很多重复的工作!幸运的是,Symfony 可以再次提供帮助。
Symfony 应用流程
Symfony 框架应用程序也使用前端控制器文件。但在内部,Symfony 负责处理每个传入的请求并弄清楚该怎么做
传入的请求由 Routing 组件 解释,并传递给返回 Response
对象的 PHP 函数。
这可能 अभी तक 没有意义,但随着你继续阅读,你将了解 路由 和 控制器:创建页面的两个基本部分。但是,随着你的进行,不要忘记,无论你的应用程序变得多么复杂,你的工作始终是相同的:从 Request 中读取信息并使用它来创建 Response。
总结:请求-响应流程
这是我们目前所学的
- 客户端(例如浏览器)发送 HTTP 请求;
- 每个请求都执行相同的文件(称为“前端控制器”);
- 前端控制器启动 Symfony 并传递请求信息;
- 在内部,Symfony 使用路由和控制器来创建页面的 Response(我们很快就会了解这些!);
- Symfony 将你的
Response
对象转换为文本头和内容(即 HTTP 响应),这些内容将被发送回客户端。