PHP回顾系列目录
- PHP基础
PHP作为为web开发而生的语言,在web开发中是一把便捷顺手的利器。将web流程中的细节梳理清楚,在实际工作中能起到提升效率和快速定位问题的作用。
本篇将简要介绍组成web请求的url(网址)、header(头部)和body(正文)三个部分,以及对应概念的PHP操作。
注: 本文所述从用户和开发者角度出发,与HTTP协议规范定义的格式描述层面不同。
请求url
请求的第一步是资源的url,例如 https://qiquanji.com/about。url包含以下信息:
- 协议, http或https。
- 端口,http默认是80, https默认443。https://qiquanji.com/about的效果同https://qiquanji.com:443/about。
- 主机名,例如www.qiquanji.com。
- 请求字符串。请求网址如:https://www.baidu.com/s?ie=utf-8&wd=flower,则请求字符串为 id=utf-8&wd=flower。请求字符串是网址的重要组成部分,动态页面会根据请求参数返回不同的结果。
浏览器 不会发送锚链接 到服务器。用curl等工具发送锚链接信息到服务端,默认被过web服务器滤掉。开发中如果需要锚链接的值,建议转换成namr=value的请求字符串,服务端捕捉后再进行处理。
PHP的几个超全局变量包含了url的所有信息,相关的超全局变量有:$_SERVER, $_GET,$_REQUEST。以下是获取网址信息的方式:
1. $_SERVER['HTTPS']为空,表示用的https协议,否则是http协议;
2. $_SERVER['SERVER_PORT']可以获取到服务器监听端口;
3. $_SERVER['SERVER_NAME']可以获取请求主机名;
4. $_GET数组包含了请求字符串的键值对。
url是了解用户请求的第一步。例如: 为了安全,将http请求重定向到https;对请求主机名进行判定,如果请求主机名非限定的域名,则拒绝:
$serverName = $_SERVER['SERVER_NAME']; // 将http流量转到https if (isset($_SERVER['HTTPS']) { header("location: https://{$serverName}:{$_SERVER['SERVER_PORT']}{$_SERVER['REQUEST_URI']}"); exit; } // 限定请求主机,可以防止恶意ip扫描带来的旁站攻击 if ($serverName !== "www.qiquanji.com" || $serverName !== "qiquanji.com") { exit("please visit with server name"); }
站点中动态页面的大部分业务逻辑都需要$_GET中的数据作为参数输入,所以$_GET数组的重要性不言而喻。静态页面一般不需要参数,有无$_GET参数影响不大。
请求header
url指示资源的位置,header提供额外的参数和选项。常见的header选项有:
- User Agent(UA),用户代理字符串,指示当前用户使用何种浏览器/工具访问页面。常见的用户代理包括浏览器、curl工具以及各种爬虫;
- Content-Type,请求的文档类型。常见的文档类型包括form表单、json和xml;
- referrer,指示用户由哪个页面引导而来;
- cookie,请求携带的cookie。由于http是无状态的协议,对于认证的会话,一般需要携带PHPSESSID, JSESSIONID等认证信息;
- Authorization,认证信息,内容包含用户名和base64后的密码。
其他header信息还包括accept/accept-encoding,以及各类自定义头部等。
头部信息为请求提供了额外的数据,让服务端程序得以了解更全面的请求信息。例如可以根据UA返回适用于IE/chrome/firefox浏览器的页面,或者根据UA跳转桌面版/移动版/wap版,以改善体验;referrer头部可用来鉴定页面来源是否合法,资源是否被盗用;对于POST/PUT等更新类请求,服务端程序需要根据Content-type来确定请求正文的类型,从而正确解码和反序列化。
站在开发角度上,PHP如何获取头部信息?
配合apache使用的PHP程序,可用 getallheaders
函数获取所有的头部信息;如果安装了PECL的http拓展,可使用 http_get_request_headers
函数获取所有头部。如果以nginx+php-fpm的方式运行PHP程序,则需要从 $_SERVER
超全局变量中获取。$_SERVER超全局变量中以 HTTP_
为前缀的键值信息即是解析到的HTTP头部,下面是一个从 $\_SERVER
超全局变量中提取http头部的函数示例:
function getAllHeaders() { $headers = []; foreach ($_SERVER as $name => $value) { if (strncmp($name, 'HTTP_', 5) === 0) { $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))); $headers[$name] = $value; } } return $headers; }
头部中的cookie信息是web开发中必须掌握的概念,计划今后单独开篇详谈, 此文中略过。
头部中的Content-type是另外一个重要的概念,一般出现在POST/PUT/PATCH等更新类的资源请求中。常规的form表单请求,内容类型的值为: “application/x-www-form-urlencoded”,即以url方式对参数键值进行解码。文件上传时,form表单需要设置 enctype=”multipart/form-data” 属性,对应的内容类型为 “multipart/form-data”。另外SOAP请求中常见 “application/xml”,Restful API中则常见 “application/json” 的内容类型。非常规form表单和文件上传请求,web服务器一般不能正确处理请求正文,需要手动在程序中对程序进行解码。
请求body
浏览器和服务器对请求的url的长度一般是有限制的,类似于文件上传的请求,把文件内容放到url上再发送到服务端的做法难以让人接受;另一方面,url的信息会直接显示在地址栏上,认证请求中的用户名和密码如果附加在url上会让人直接一览无余,直观上的安全性不强。对于类似的请求,将请求正文主题放在专用的正文字段中,显得合理且必要。
常见的form表单请求(包含文件上传),PHP内置了直接的支持。表单字段可以使用 $_POST, $_REQUEST
超全局变量获取值,上传的文件通过 $_FILES
获取。开发中常会接触到这几个超全局变量,以获取用户提交过来的数据。
其他非表单形式的请求,需要从 $HTTP_RAW_POST_DATA
(仅限PHP7之前)或者 “php://input”(推荐方式) 中读取。 例如SOAP业务的交互,基本上以xml为交互内容载体,则需要先提取出请求的xml内容,然后解析。以下是将xml请求解析成数组的函数示例:
function getRequestData() { $data = file_get_contents("php://input"); $xml = simplexml_load_string($data); $json = json_encode($xml, JSON_UNESCAPED_UNICODE); return json_decode($json, true); }
Restful API中常用json作为数据交换载体,也需要做类似的解析。
其他
一个常见的问题,用户端的IP如何获取?
HTTP是TCP上层的应用层协议,而TCP是可靠的端对端通信协议。客户端和服务器在通信链路建立时,需要知道对方的IP和通信端口。web服务器收到请求后,转发请求到PHP端时会传递IP相关信息,PHP可以从 $_SERVER
超全局变量中读取。以下是函数示例:
public function getUserIP() { return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; }
更多环境变量可以参考 $_SERVER
超全局变量。
总结
了解以上web请求的三部分,可以帮助web开发人员掌握用户请求的详细信息,为后续的业务逻辑处理扫清第一道障碍。
原文链接:https://www.qiquanji.com/post/4843.html
本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。
微信扫码关注
更新实时通知