PHP作為為web開發而生的語言,在web開發中是一把便捷順手的利器。將web流程中的細節梳理清楚,在實際工作中能起到提升效率和快速定位問題的作用。
皇家國際:www.lanshiliu.com
本篇將簡要介紹組成web請求的url(網址)、header(頭部)和body(正文)三個部分,以及對應概念的PHP操作。
注: 本文所述從使用者和開發者角度出發,與HTTP協議規範定義的格式描述層面不同。
請求url
請求的第一步是資源的url,
url包含以下資訊:\
-
協議, http或https。
-
埠,http預設是80, https預設443。
-
主機名,例如www.tlanyan.me。
-
請求字串。請求網址如:
https://www.baidu.com/s?ie=utf-8&wd=fl...,
則請求字串為 id=utf-8&wd=flower。
請求字串是網址的重要組成部分,
動態頁面會根據請求引數返回不同的結果。
瀏覽器不會傳送錨連結到伺服器。
用curl等工具傳送錨連結資訊到服務端,預設被過web伺服器濾掉。開發中如果需要錨連結的值,建議轉換成namr=value的請求字串,服務端捕捉後再進行處理。
PHP的幾個超全域性變數包含了url的所有資訊,
相關的超全域性變數有:$_SERVER, $_GET,$_REQUEST。
以下是獲取網址資訊的方式:\
$_SERVER['HTTPS']為空,表示用的https協議,否則是http協議;$_SERVER['SERVER_PORT']可以獲取到伺服器監聽埠;``$_SERVER['SERVER_NAME']可以獲取請求主機名;``$_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.tlanyan.me" || $serverName !== "tlanyan.me") {`` 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()
Restful API中常用json作為資料交換載體,也需要做類似的解析。\
其他
一個常見的問題,使用者端的IP如何獲取?
HTTP是TCP上層的應用層協議,而TCP是可靠的端對端通訊協議。客戶端和伺服器在通訊鏈路建立時,需要知道對方的IP和通訊埠。web伺服器收到請求後,轉發請求到PHP端時會傳遞IP相關資訊,PHP可以從 $_SERVER 超全域性變數中讀取。以下是函式示例:
public function getUserIP()
更多環境變數可以參考 $_SERVER 超全域性變數。\
總結
瞭解以上web請求的三部分,可以幫助web開發人員掌握使用者請求的詳細資訊,為後續的業務邏輯處理掃清第一道障礙。
原文地址:https://tlanyan.me/php-review-web-request/
本作品採用《CC 協議》,轉載必須註明作者和本文連結