先決條件: | 基本的計(jì)算機(jī)素養(yǎng)。 基本了解Web服務(wù)器是什么。 |
---|---|
目的: | 要了解動(dòng)態(tài)網(wǎng)站中的客戶端 - 服務(wù)器交互,特別是需要由服務(wù)器端代碼執(zhí)行哪些操作。 |
在討論中沒有真正的代碼,因?yàn)槲覀冞€沒有選擇使用Web框架來編寫我們的代碼! 然而,此討論仍然非常相關(guān),因?yàn)樗枋龅男袨楸仨氂赡姆?wù)器端代碼實(shí)現(xiàn),無(wú)論您選擇哪種編程語(yǔ)言或Web框架。
網(wǎng)絡(luò)瀏覽器使用網(wǎng)絡(luò)服務(wù)器,使用 H yper strong> T ext T 傳輸 P 協(xié)議( HTTP"> HTTP )。 當(dāng)您點(diǎn)擊網(wǎng)頁(yè)上的鏈接,提交表單或運(yùn)行搜索時(shí),瀏覽器會(huì)向服務(wù)器發(fā)送 HTTP請(qǐng)求。
此請(qǐng)求包括:
GET
: Get a specific resource (e.g. an HTML file containing information about a product, or a list of products).?POST
: Create a new resource (e.g. add a new article to a wiki, add a new contact to a database).?HEAD
: Get the metadata information about a specific resource without getting the body like GET
would. You might for example use a HEAD
request to find out the last time a resource was updated, and then only use the (more "expensive") GET
request to download the resource if it has changed.?PUT
: Update an existing resource (or create a new one if it doesn't exist).DELETE
: Delete?the specified resource.TRACE
, OPTIONS
, CONNECT
, PATCH
: These verbs are for less common/advanced tasks, so we won't cover them here.GET
requests encode data in the?URL sent to the server by adding name/value pairs onto the end of it — for example http://mysite.com?name=Fred&age=11
. You always have a question mark (?
) separating the rest of the URL from the URL parameters, an equals sign (=
) separating each name from its associated value, and an ampersand (&
) separating each pair.? URL parameters are inherently "insecure" as they can be changed by users and then resubmitted. As a result URL parameters/GET
requests are not used for requests that update data on the server.POST
data. POST
requests add new resources, the data for which is encoded within?the request body.Web服務(wù)器等待客戶端請(qǐng)求消息,在到達(dá)時(shí)處理它們,并使用HTTP響應(yīng)消息回復(fù)Web瀏覽器。 響應(yīng)包含指示請(qǐng)求是否成功的 HTTP響應(yīng)狀態(tài)代碼(例如" 200 OK
"如果找不到資源," 403 ForBidden
"如果用戶未授權(quán) 查看資源等)。 對(duì) GET
請(qǐng)求的成功響應(yīng)的正文將包含所請(qǐng)求的資源。
當(dāng)HTML頁(yè)面返回時(shí),它由Web瀏覽器呈現(xiàn)。 作為處理的一部分,瀏覽器可以發(fā)現(xiàn)到其他資源的鏈接(例如,通常引用JavaScript和CSS頁(yè)面的HTML頁(yè)面),并且將發(fā)送單獨(dú)的HTTP請(qǐng)求以下載這些文件。
靜態(tài)和動(dòng)態(tài)網(wǎng)站(在以下部分討論)使用完全相同的通信協(xié)議/模式。
您可以通過點(diǎn)擊鏈接或在網(wǎng)站(如搜索引擎首頁(yè))上進(jìn)行搜索來創(chuàng)建一個(gè)簡(jiǎn)單的 GET
請(qǐng)求。 例如,當(dāng)您在MDN上執(zhí)行"客戶端服務(wù)器概述"術(shù)語(yǔ)搜索時(shí)發(fā)送的HTTP請(qǐng)求將看起來很像下面顯示的文本(它將不完全相同,因?yàn)橄⒌牟糠秩Q于您的瀏覽器/設(shè)置 )。
HTTP消息的格式在"網(wǎng)絡(luò)標(biāo)準(zhǔn)"( RFC7230 )中定義。 你不需要知道這個(gè)級(jí)別的細(xì)節(jié),但至少現(xiàn)在你知道這一切都來自哪里!
請(qǐng)求的每一行包含有關(guān)它的信息。 第一部分稱為標(biāo)頭,其中包含有關(guān)請(qǐng)求的有用信息,其方式與 HTML頭包含的有用信息相同 有關(guān)HTML文檔的信息(但不是實(shí)際內(nèi)容本身,它在正文中):
GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1 Host: developer.mozilla.org Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Referer: https://developer.mozilla.org/en-US/ Accept-Encoding: gzip, deflate, sdch, br Accept-Language: en-US,en;q=0.8,es;q=0.6 Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _gat=1; _ga=GA1.2.1688886003.1471911953; ffo=true
第一行和第二行包含我們上面討論的大多數(shù)信息:
GET
)./en-US/search
).q=client%2Bserver%2Boverview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev
).HTTP/1.1
).最后一行包含關(guān)于客戶端cookie的信息 - 您可以看到,在這種情況下,cookie包括用于管理會(huì)話的ID( Cookie:sessionid = 6ynxs23n521lu21b1t136rhbv7ezngie; ...
)。
其余行包含有關(guān)所使用的瀏覽器的信息以及它可以處理的響應(yīng)類型。 例如,你可以看到:
User-Agent
) is Mozilla Firefox (Mozilla/5.0
).Accept-Encoding: gzip
).Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7
) and languages (Accept-Language: de,en;q=0.7,en-us;q=0.3
).Referer
line indicates the address of the web page that contained the link to this resource (i.e. the origin of the request, https://developer.mozilla.org/en-US/
).HTTP請(qǐng)求也可以有一個(gè)主體,但在這種情況下它是空的。
此請(qǐng)求的響應(yīng)的第一部分如下所示。 標(biāo)題包含如下信息:
200 OK
, which tells us that the request succeeded.text/html
formatted (Content-Type
).Content-Type: text/html; charset=utf-8
).Content-Length: 41823
).在消息結(jié)束時(shí),我們會(huì)看到正文內(nèi)容 - 其中包含請(qǐng)求返回的實(shí)際HTML。
HTTP/1.1 200 OK Server: Apache X-Backend-Server: developer1.webapp.scl3.mozilla.com Vary: Accept,Cookie, Accept-Encoding Content-Type: text/html; charset=utf-8 Date: Wed, 07 Sep 2016 00:11:31 GMT Keep-Alive: timeout=5, max=999 Connection: Keep-Alive X-Frame-Options: DENY Allow: GET X-Cache-Info: caching Content-Length: 41823 <!DOCTYPE html> <html lang="en-US" dir="ltr" class="redesign no-js" ?data-ffo-opensanslight=false data-ffo-opensans=false > <head prefix="og: http://ogp.me/ns#"> ? <meta charset="utf-8"> ? <meta http-equiv="X-UA-Compatible" content="IE=Edge"> ? <script>(function(d) { d.className = d.className.replace(/\bno-js/, ''); })(document.documentElement);</script> ? ...
請(qǐng)求的其余部分包括關(guān)于響應(yīng)的信息(例如,當(dāng)它被生成時(shí)),服務(wù)器,以及它如何期望瀏覽器處理頁(yè)面(例如 X-Frame-Options:DENY
瀏覽器不允許此網(wǎng)頁(yè)嵌入 < iframe>
)。
當(dāng)您提交包含要保存在服務(wù)器上的信息的表單時(shí),會(huì)生成HTTP POST
。
下面的文本顯示了當(dāng)用戶在此網(wǎng)站上提交新的配置文件詳細(xì)信息時(shí)發(fā)出的HTTP請(qǐng)求。 請(qǐng)求的格式與之前顯示的 GET
請(qǐng)求示例幾乎相同,但第一行將此請(qǐng)求標(biāo)識(shí)為 POST
。
POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1 Host: developer.mozilla.org Connection: keep-alive Content-Length: 432 Pragma: no-cache Cache-Control: no-cache Origin: https://developer.mozilla.org Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Content-Type: application/x-www-form-urlencoded Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Referer: https://developer.mozilla.org/en-US/profiles/hamishwillee/edit Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.8,es;q=0.6 Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; _gat=1; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _ga=GA1.2.1688886003.1471911953; ffo=true csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwillee&user-fullname=Hamish+Willee&user-title=&user-organization=&user-location=Australia&user-locale=en-US&user-timezone=Australia%2FMelbourne&user-irc_nickname=&user-interests=&user-expertise=&user-twitter_url=&user-stackoverflow_url=&user-linkedin_url=&user-mozillians_url=&user-facebook_url=
主要的區(qū)別是URL沒有任何參數(shù)。 您可以看到,表單中的信息在請(qǐng)求的正文中進(jìn)行了編碼(例如,新用戶的全名通過以下方式設(shè)置:& user-fullname = Hamish + Willee
)。
請(qǐng)求的響應(yīng)如下所示。 狀態(tài)代碼" 302 Found
"告訴瀏覽器該帖子成功,并且它必須發(fā)出第二個(gè)HTTP請(qǐng)求來加載在 Location
字段中指定的頁(yè)面。 該信息類似于對(duì) GET
請(qǐng)求的響應(yīng)。
HTTP/1.1 302 FOUND Server: Apache X-Backend-Server: developer3.webapp.scl3.mozilla.com Vary: Cookie Vary: Accept-Encoding Content-Type: text/html; charset=utf-8 Date: Wed, 07 Sep 2016 00:38:13 GMT Location: https://developer.mozilla.org/en-US/profiles/hamishwillee Keep-Alive: timeout=5, max=1000 Connection: Keep-Alive X-Frame-Options: DENY X-Cache-Info: not cacheable; request wasn't a GET or HEAD Content-Length: 0
注意:這些示例中顯示的HTTP響應(yīng)和請(qǐng)求是使用 Fiddler a>應(yīng)用程序,但您可以使用網(wǎng)絡(luò)嗅探器獲取類似的信息(例如 http://web-sniffer.net/ >)或使用瀏覽器擴(kuò)展程序,如 HttpFox 。 你可以自己嘗試一下。 使用任何鏈接的工具,然后瀏覽網(wǎng)站并編輯個(gè)人資料信息以查看不同的請(qǐng)求和響應(yīng)。 大多數(shù)新式瀏覽器還有監(jiān)控網(wǎng)絡(luò)請(qǐng)求的工具(例如,F(xiàn)irefox中的網(wǎng)絡(luò)監(jiān)控工具) )。
每當(dāng)請(qǐng)求特定資源時(shí),靜態(tài)網(wǎng)站是從服務(wù)器返回相同的硬編碼內(nèi)容的網(wǎng)站。 例如,如果您在 /static/myproduct1.html
上有一個(gè)關(guān)于產(chǎn)品的頁(yè)面,則該同一頁(yè)面將返回給每個(gè)用戶。 如果您向網(wǎng)站添加其他類似產(chǎn)品,則需要添加其他網(wǎng)頁(yè)(例如, myproduct2.html
)等。 這可能開始真的效率低下 - 當(dāng)你得到成千上萬(wàn)的產(chǎn)品頁(yè)面會(huì)發(fā)生什么? 您會(huì)在每個(gè)網(wǎng)頁(yè)(基本網(wǎng)頁(yè)模板,結(jié)構(gòu)等)上重復(fù)大量代碼,如果您想更改網(wǎng)頁(yè)結(jié)構(gòu)的任何內(nèi)容,例如添加新的"相關(guān)產(chǎn)品"部分, 必須單獨(dú)更改每個(gè)頁(yè)面。
注意:如果您的網(wǎng)頁(yè)數(shù)量較少,并且希望向每個(gè)用戶發(fā)送相同的內(nèi)容,則靜態(tài)網(wǎng)站非常棒。 然而,隨著頁(yè)面數(shù)量變大,它們可以具有顯著的維護(hù)成本。
讓我們回顧一下這是如何工作的,再看一下我們?cè)谏弦黄恼轮锌吹降撵o態(tài)網(wǎng)站架構(gòu)圖。
當(dāng)用戶想要導(dǎo)航到頁(yè)面時(shí),瀏覽器發(fā)送HTTP GET
請(qǐng)求,指定其HTML頁(yè)面的URL。 服務(wù)器從其文件系統(tǒng)檢索所請(qǐng)求的文檔,并返回包含該文檔和 HTTP響應(yīng)的HTTP響應(yīng) 狀態(tài)碼"(表示成功)的" 200 OK
"。 如果文件不存在于服務(wù)器上,服務(wù)器可能會(huì)返回不同的狀態(tài)代碼,例如" 404 Not Found
",如果文件存在,則會(huì)返回" 301 Moved Permanently
但已重定向到其他位置。
靜態(tài)站點(diǎn)的服務(wù)器將只需要處理GET請(qǐng)求,因?yàn)榉?wù)器不存儲(chǔ)任何可修改的數(shù)據(jù)。 它也不會(huì)根據(jù)HTTP請(qǐng)求數(shù)據(jù)(例如網(wǎng)址參數(shù)或Cookie)更改其響應(yīng)。
然而,當(dāng)學(xué)習(xí)服務(wù)器端編程時(shí),了解靜態(tài)站點(diǎn)的工作原理仍然有用,因?yàn)閯?dòng)態(tài)站點(diǎn)以完全相同的方式處理對(duì)靜態(tài)文件(CSS,JavaScript,靜態(tài)圖像等)的請(qǐng)求。
動(dòng)態(tài)網(wǎng)站是一種可根據(jù)特定請(qǐng)求網(wǎng)址和數(shù)據(jù)生成和返回內(nèi)容的網(wǎng)站(而不是始終為特定網(wǎng)址返回相同的硬編碼文件)。 使用產(chǎn)品站點(diǎn)的示例,服務(wù)器將產(chǎn)品"數(shù)據(jù)"存儲(chǔ)在數(shù)據(jù)庫(kù)中而不是單個(gè)HTML文件。 當(dāng)接收到對(duì)產(chǎn)品的HTTP GET
請(qǐng)求時(shí),服務(wù)器確定產(chǎn)品ID,從數(shù)據(jù)庫(kù)獲取數(shù)據(jù),然后通過將數(shù)據(jù)插入到HTML模板中來構(gòu)造響應(yīng)的HTML頁(yè)面。 這相對(duì)于靜態(tài)網(wǎng)站有以下優(yōu)點(diǎn):
使用數(shù)據(jù)庫(kù)允許以容易擴(kuò)展,可修改和可搜索的方式有效地存儲(chǔ)產(chǎn)品信息。
使用HTML模板可以很容易地更改HTML結(jié)構(gòu),因?yàn)檫@只需要在一個(gè)地方,在單個(gè)模板中完成,而不是跨越可能數(shù)千個(gè)靜態(tài)頁(yè)面。
本部分提供了"動(dòng)態(tài)"HTTP請(qǐng)求和響應(yīng)周期的逐步概述,基于我們?cè)谏弦黄恼轮性敿?xì)介紹的內(nèi)容。 為了"保持事物真實(shí)",我們將使用運(yùn)動(dòng)團(tuán)隊(duì)經(jīng)理網(wǎng)站的上下文,教練可以在HTML表單中選擇他們的球隊(duì)名稱和團(tuán)隊(duì)規(guī)模,并獲得他們下一場(chǎng)比賽的建議"最佳陣容"。
下圖顯示了"團(tuán)隊(duì)教練"網(wǎng)站的主要元素,以及教練訪問其"最佳團(tuán)隊(duì)"列表時(shí)的操作順序的編號(hào)標(biāo)簽。 網(wǎng)站的動(dòng)態(tài)部分是 Web應(yīng)用程序(這是我們將如何參考處理HTTP請(qǐng)求和返回HTTP響應(yīng)的服務(wù)器端代碼),數(shù)據(jù)庫(kù) em>,其中包含有關(guān)玩家,團(tuán)隊(duì),教練及其關(guān)系以及 HTML模板的信息。
在教練提交隊(duì)伍名稱和球員數(shù)量的表格后,操作的順序是:
GET
request to the server using the base URL for the resource (/best
) and encoding the team and player number either as URL parameters (e.g. /best?team=my_team_name&show=11)
?or as part of the URL pattern (e.g.?/best/my_team_name/11/
).?A GET
request is used because the request is only fetching data (not modifying data)./best/
) and finds out?the required team name and number of players from the URL. The Web Application?then gets the required information from the database (using additional "internal" parameters to define which players are "best", and possibly also getting the identity of the logged in coach from a client-side cookie).更新數(shù)據(jù)庫(kù)中的記錄的操作將類似地處理,除了像任何數(shù)據(jù)庫(kù)更新一樣,來自瀏覽器的HTTP請(qǐng)求應(yīng)被編碼為 POST
請(qǐng)求。
的作業(yè)是接收HTTP請(qǐng)求并返回HTTP響應(yīng)。 雖然與數(shù)據(jù)庫(kù)交互以獲取或更新信息是非常常見的任務(wù),但代碼可以同時(shí)做其他事情,或根本不與數(shù)據(jù)庫(kù)交互。
Web應(yīng)用程序可能執(zhí)行的額外任務(wù)的一個(gè)好例子是向用戶發(fā)送電子郵件,以確認(rèn)他們?cè)诰W(wǎng)站上的注冊(cè)。 該站點(diǎn)還可能執(zhí)行日志記錄或其他操作。
服務(wù)器端網(wǎng)站代碼不必在響應(yīng)中返回HTML片段/文件。 它可以改為動(dòng)態(tài)創(chuàng)建和返回其他類型的文件(文本,PDF,CSV等)甚至數(shù)據(jù)(JSON,XML等)。
將數(shù)據(jù)返回到Web瀏覽器以便它可以動(dòng)態(tài)更新自己的內(nèi)容的想法( AJAX )已經(jīng)存在了一段時(shí)間。 最近的"單頁(yè)應(yīng)用程序"已經(jīng)變得流行,整個(gè)網(wǎng)站都用一個(gè)HTML文件編寫,并在需要時(shí)動(dòng)態(tài)更新。 使用這種應(yīng)用程序創(chuàng)建的網(wǎng)站將大量的計(jì)算成本從服務(wù)器推送到Web瀏覽器,并且可能導(dǎo)致網(wǎng)站看起來更像本地應(yīng)用程序(響應(yīng)速度快等)。
服務(wù)器端Web框架使編寫代碼來處理上面描述的操作更容易。
它們執(zhí)行的最重要的操作之一是提供簡(jiǎn)單的機(jī)制來將不同資源/頁(yè)面的URL映射到特定的處理程序函數(shù)。 這使得更容易保持與每種類型的資源相關(guān)聯(lián)的代碼分離。 它還具有維護(hù)方面的優(yōu)點(diǎn),因?yàn)槟梢愿挠糜谠谝粋€(gè)位置提供特定功能的URL,而無(wú)需更改處理函數(shù)。
例如,考慮下面的將兩個(gè)URL模式映射到兩個(gè)視圖函數(shù)的Django(Python)代碼。 第一種模式確保具有資源URL / best
的HTTP請(qǐng)求將被傳遞到 views
模塊中的 index()
。 具有模式" / best / junior
"的請(qǐng)求將被傳遞到 junior()
視圖函數(shù)。
# file: best/urls.py # from django.conf.urls import url from . import views urlpatterns = [ # example: /best/ url(r'^$', views.index), # example: /best/junior/ url(r'^junior/$', views.junior), ]
注意: url()
函數(shù)中的第一個(gè)參數(shù)可能看起來有點(diǎn)奇怪(例如 r\'^ junior / $\'
), 稱為"正則表達(dá)式"(RegEx或RE)的模式匹配技術(shù)。 你不需要知道正則表達(dá)式在這一點(diǎn)上的工作方式,除了它們?cè)试S我們匹配URL中的模式(而不是上面的硬編碼值),并將它們用作我們的視圖函數(shù)中的參數(shù)。 例如,一個(gè)真正簡(jiǎn)單的RegEx可能會(huì)說"匹配一個(gè)單一的大寫字母,后跟4到7個(gè)小寫字母"。
Web框架還使得視圖函數(shù)從數(shù)據(jù)庫(kù)獲取信息變得容易。 我們的數(shù)據(jù)結(jié)構(gòu)在模型中定義,這些模型是定義要存儲(chǔ)在底層數(shù)據(jù)庫(kù)中的字段的Python類。 如果我們有一個(gè)名為 Team 的模型,其字段為" team_type ",那么我們可以使用一個(gè)簡(jiǎn)單的查詢語(yǔ)法來取回所有具有特定類型的團(tuán)隊(duì)。
下面的示例獲取了所有具有(區(qū)分大小寫) team_type
"junior"的所有小組的列表 - 請(qǐng)注意格式:字段名稱( team_type
)后跟雙下劃線 ,然后是要使用的匹配類型(在本例中為 exact
)。 還有許多其他類型的火柴,我們可以菊花鏈。 我們還可以控制返回的結(jié)果的順序和數(shù)量。
#best/views.py from django.shortcuts import render from .models import Team def junior(request): ? ? list_teams = Team.objects.filter(team_type__exact="junior") ? ? context = {'list': list_teams} ? ? return render(request, 'best/index.html', context)
在 junior()
函數(shù)獲取初級(jí)團(tuán)隊(duì)列表之后,它調(diào)用 render()
函數(shù),傳遞原始的 HttpRequest
,以及定義要包括在模板中的信息的"上下文"對(duì)象。 render()
函數(shù)是一個(gè)方便的函數(shù),它使用上下文和HTML模板生成HTML,并在 HttpResponse
對(duì)象中返回它。
顯然,Web框架可以幫助您完成許多其他任務(wù)。 我們?cè)谙乱黄恼轮杏懻摿烁嗟暮锰幒鸵恍┝餍械腤eb框架選擇。
在這一點(diǎn)上,您應(yīng)該對(duì)服務(wù)器端代碼必須執(zhí)行的操作有一個(gè)很好的概述,并且了解服務(wù)器端Web框架可以使這更容易的一些方法。
在下面的模塊中,我們將幫助您為您的第一個(gè)網(wǎng)站選擇最好的Web框架。
更多建議: