Web框架本質
對於全部的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# !/usr/bin/env python
# -*- coding:utf-8 -*-
# __Author__ Jianer
import
socket
def
handle_request(client):
buf
=
client.recv(
1024
)
client.send(
"HTTP/1.1 200 OK\r\n\r\n"
)
client.send(
"Hello, Seven"
)
def
main():
sock
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((
'localhost'
,
8000
))
sock.listen(
5
)
while
True
:
connection, address
=
sock.accept()
handle_request(connection)
connection.close()
if
__name__
=
=
'__main__'
:
main()
|
socket流程不細說,關於web框架的一些闡釋python
上述經過socket來實現了其本質,而對於真實開發中的python web程序來講,通常會分爲兩部分:服務器程序和應用程序。服務器程序負責對socket服務器進行封裝,並在請求到來時,對請求的各類數據進行整理。應用程序則負責具體的邏輯處理。爲了方便應用程序的開發,就出現了衆多的Web框架,例如:Django、Flask、web.py 等。不一樣的框架有不一樣的開發方式,可是不管如何,開發出的應用程序都要和服務器程序配合,才能爲用戶提供服務。這樣,服務器程序就須要爲不一樣的框架提供不一樣的支持。這樣混亂的局面不管對於服務器仍是框架,都是很差的。對服務器來講,須要支持各類不一樣框架,對框架來講,只有支持它的服務器才能被開發出的應用使用。這時候,標準化就變得尤其重要。咱們能夠設立一個標準,只要服務器程序支持這個標準,框架也支持這個標準,那麼他們就能夠配合使用。一旦標準肯定,雙方各自實現。這樣,服務器能夠支持更多支持標準的框架,框架也可使用更多支持標準的服務器。web
WSGI(Web Server Gateway Interface)是一種規範,它定義了使用python編寫的web app與web server之間接口格式,實現web app與web server間的解耦。瀏覽器
python標準庫提供的獨立WSGI服務器稱爲wsgiref。緩存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# !/usr/bin/env python
# -*- coding:utf-8 -*-
# __Author__ Jianer
from
wsgiref.simple_server
import
make_server
def
application(request_info,response_info):
print
(request_info)
# 包裝了請求的諸多信息
response_info(
'200 OK'
,[(
'Content-Type'
,
'text/html'
)])
# 反饋響應信息
return
[b
'<h1>Hello World</h1>'
]
httpd
=
make_server(
"127.0.0.1"
,
8080
, application)
# 開啓循環監聽http請求
httpd.serve_forever()
|
自定製Web框架
經過python標準庫提供的獨立WSGI服務器能夠定製一個簡單的小型Web框架服務器
1、框架網絡
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
27
28
29
30
31
32
33
34
35
36
37
38
39
|
from
wsgiref.simple_server
import
make_server
def
index():
return
b
'index'
def
login():
return
b
'login'
def
routers():
urlpatterns
=
(
(
'/index/'
, index),
(
'/login/'
, login),
)
return
urlpatterns
def
RunServer(environ, start_response):
start_response(
'200 OK'
, [(
'Content-Type'
,
'text/html'
)])
url
=
environ[
'PATH_INFO'
]
urlpatterns
=
routers()
func
=
None
for
item
in
urlpatterns:
if
item[
0
]
=
=
url:
func
=
item[
1
]
break
if
func:
return
[func()]
else
:
return
[b
'404 not found'
]
if
__name__
=
=
'__main__'
:
httpd
=
make_server(
'127.0.0.1'
,
8080
,RunServer)
print
(
"Serving HTTP on port 8080..."
)
httpd.serve_forever()
|
2、模板併發
在上一步驟中,對於全部的login、index均返回給用戶瀏覽器一個簡單的字符串,在現實的Web請求中通常會返回一個複雜的符合HTML規則的字符串,因此通常將要返回給用戶的HTML寫在指定文件中,而後再返回。以下:app
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <h1>Index</h1> </body> </html>
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <form> <input type="text" /> <input type="text" /> <input type="submit" /> </form> </body> </html>
繼而、框架
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
from
wsgiref.simple_server
import
make_server
def
index():
# return 'index'
f
=
open
(
'index.html'
,
'rb'
)
data
=
f.read()
return
data
def
login():
# return 'login'
f
=
open
(
'login.html'
,
'rb'
)
data
=
f.read()
return
data
def
routers():
urlpatterns
=
(
(
'/index/'
, index),
(
'/login/'
, login),
)
return
urlpatterns
def
run_server(environ, start_response):
start_response(
'200 OK'
, [(
'Content-Type'
,
'text/html'
)])
url
=
environ[
'PATH_INFO'
]
urlpatterns
=
routers()
func
=
None
for
item
in
urlpatterns:
if
item[
0
]
=
=
url:
func
=
item[
1
]
break
if
func:
return
[func()]
else
:
return
[b
'404 not found'
]
if
__name__
=
=
'__main__'
:
httpd
=
make_server(
'127.0.0.1'
,
8080
, run_server)
print
(
"Serving HTTP on port 8080..."
)
httpd.serve_forever()
|
對於上述代碼,雖然能夠返回給用戶HTML的內容以現實複雜的頁面,可是仍是存在問題:如何給用戶返回動態內容?
代碼更新中......
HTTP協議
1、簡介
HTTP(hypertext transport protocol),即超文本傳輸協議。這個協議詳細規定了瀏覽器和萬維網服務器之間互相通訊的規則。
HTTP就是一個通訊規則,通訊規則規定了客戶端發送給服務器的內容格式,也規定了服務器發送給客戶端的內容格式。其實學習的就是這個兩個格式!客戶端發送給服務器的格式叫「請求協議」;服務器發送給客戶端的格式叫「響應協議」。即HTTP基於請求/響應模式。
- HTTP是無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。
- HTTP是無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,這樣可能致使每次鏈接傳送的數據量增大。另外一方面,在服務器不須要先前信息時它的應答就較快。
URL:統一資源定位符,就是一個網址:協議名://域名:端口/路徑
- HTTP是無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。
- HTTP是無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,這樣可能致使每次鏈接傳送的數據量增大。另外一方面,在服務器不須要先前信息時它的應答就較快。
2、請求協議
一個HTTP"客戶端"是一個應用程序(Web瀏覽器或其餘任何客戶端),經過鏈接到服務器達到向服務器發送一個或多個HTTP的請求的目的。
客戶端發送一個HTTP請求到服務器的請求消息包括如下格式:請求行(request line)、請求頭部(header)、空行和請求數據四個部分組成,下圖給出了請求報文的通常格式。
瀏覽器發送給服務器的內容就這個格式的,若是不是這個格式服務器將沒法解讀!在HTTP協議中,請求有不少請求方法,其中最爲經常使用的就是GET和POST。
A、GET請求
HTTP默認的請求方法就是GET
- 沒有請求體
- 數據必須在1K以內!
- GET請求數據會暴露在瀏覽器的地址欄中
GET請求經常使用的操做:
- 在瀏覽器的地址欄中直接給出URL,那麼就必定是GET請求
- 點擊頁面上的超連接也必定是GET請求
- 提交表單時,表單默認使用GET請求,但能夠設置爲POST
如在一個網頁中右擊檢查點擊network,能夠看到一些請求信息並做出一些解釋
- GET 127.0.0.1:8080/login HTTP/1.1:GET請求,請求服務器路徑爲 127.0.0.1:8080/login ,協議爲1.1;
- Host:localhost:請求的主機名爲localhost;
- *User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0:與瀏覽器和OS相關的信息。有些網站會顯示用戶的系統版本和瀏覽器版本信息,這都是經過獲取User-Agent頭信息而來的;
- Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8:告訴服務器,當前客戶端能夠接收的文檔類型,其實這裏包含了*/*,就表示什麼均可以接收;
- Accept-Language: zh-cn,zh;q=0.5:當前客戶端支持的語言,能夠在瀏覽器的工具選項中找到語言相關信息;
- Accept-Encoding: gzip, deflate:支持的壓縮格式。數據在網絡上傳遞時,可能服務器會把數據壓縮後再發送;
- Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7:客戶端支持的編碼;
- Connection: keep-alive:客戶端支持的連接方式,保持一段時間連接,默認爲3000ms;
- Cookie: JSESSIONID=369766FDF6220F7803433C0B2DE36D98:由於不是第一次訪問這個地址,因此會在請求中把上一次服務器響應中發送過來的Cookie在請求中一併發送去過;這個Cookie的名字爲JSESSIONID,而後在講會話是講究它!
B、POST請求
- 數據不會出如今地址欄中
- 數據的大小沒有上限
- 有請求體
- 請求體中若是存在中文,會使用URL編碼!
# 例如:username=%E5%BC%A0%E4%B8%89&password=123 咱們都知道Http協議中參數的傳輸是"key=value"這種簡直對形式的,若是要傳多個參數就須要用「&」符號對鍵值對進行分割。如"?name1=value1&name2=value2",這樣在服務端在收到這種字符串的時候,會用「&」分割出每個參數,而後再用「=」來分割出參數值。 針對「name1=value1&name2=value2」咱們來講一下客戶端到服務端的概念上解析過程: 上述字符串在計算機中用ASCII嗎表示爲: 6E616D6531 3D 76616C756531 26 6E616D6532 3D 76616C756532。 6E616D6531:name1 3D:= 76616C756531:value1 26:& 6E616D6532:name2 3D:= 76616C756532:value2 服務端在接收到該數據後就能夠遍歷該字節流,首先一個字節一個字節的吃,當吃到3D這字節後,服務端就知道前面吃得字節表示一個key,再想後吃,若是遇到26,說明從剛纔吃的3D到26子節之間的是上一個key的value,以此類推就能夠解析出客戶端傳過來的參數。 如今有這樣一個問題,若是個人蔘數值中就包含=或&這種特殊字符的時候該怎麼辦。 好比說「name1=value1」,其中value1的值是「va&lu=e1」字符串,那麼實際在傳輸過程當中就會變成這樣「name1=va&lu=e1」。咱們的本意是就只有一個鍵值對,可是服務端會解析成兩個鍵值對,這樣就產生了奇異。 如何解決上述問題帶來的歧義呢?解決的辦法就是對參數進行URL編碼 URL編碼只是簡單的在特殊字符的各個字節前加上%,例如,咱們對上述會產生奇異的字符進行URL編碼後結果:「name1=va%26lu%3D」,這樣服務端會把緊跟在「%」後的字節當成普通的字節,就是不會把它當成各個參數或鍵值對的分隔符。
POST請求是能夠有體的,而GET請求不能有請求體。
- Referer: http://localhost:8080/hello/index.jsp:請求來自哪一個頁面,例如你在百度上點擊連接到了這裏,那麼Referer:http://www.baidu.com;若是你是在瀏覽器的地址欄中直接輸入的地址,那麼就沒有Referer這個請求頭了;
- Content-Type: application/x-www-form-urlencoded:表單的數據類型,說明會使用url格式編碼數據;url編碼的數據都是以「%」爲前綴,後面跟隨兩位的16進制。
- Content-Length:13:請求體的長度,這裏表示13個字節。
- keyword=hello:請求體內容!hello是在表單中輸入的數據,keyword是表單字段的名字。
Referer請求頭是比較有用的一個請求頭,它能夠用來作統計工做,也能夠用來作防盜鏈。
統計工做:我公司網站在百度上作了廣告,但不知道在百度上作廣告對咱們網站的訪問量是否有影響,那麼能夠對每一個請求中的Referer進行分析,若是Referer爲百度的不少,那麼說明用戶都是經過百度找到咱們公司網站的。
防盜鏈:我公司網站上有一個下載連接,而其餘網站盜鏈了這個地址,例如在我網站上的index.html頁面中有一個連接,點擊便可下載JDK7.0,但有某我的的微博中盜鏈了這個資源,它也有一個連接指向咱們網站的JDK7.0,也就是說登陸它的微博,點擊連接就能夠從我網站上下載JDK7.0,這致使咱們網站的廣告沒有看,但下載的倒是我網站的資源。這時可使用Referer進行防盜鏈,在資源被下載以前,咱們對Referer進行判斷,若是請求來自本網站,那麼容許下載,若是非本網站,先跳轉到本網站看廣告,而後再容許下載。
3、響應協議
HTTP響應也由四個部分組成,分別是:狀態行、消息報頭、空行和響應正文。
- HTTP/1.1 200 OK:響應協議爲HTTP1.1,狀態碼爲200,表示請求成功,OK是對狀態碼的解釋;
- Server:WSGIServer/0.2 CPython/3.5.2:服務器的版本信息;
- Content-Type: text/html;charset=UTF-8:響應體使用的編碼爲UTF-8;
- Content-Length: 724:響應體爲724字節;
- Set-Cookie: JSESSIONID=C97E2B4C55553EAB46079A4F263435A4; Path=/hello:響應給客戶端的Cookie;
- Date: Wed, 25 Sep 2012 04:15:03 GMT:響應的時間,這可能會有8小時的時區差;
A、狀態碼
響應頭對瀏覽器來講很重要,它說明了響應的真正含義。例如200表示響應成功了,302表示重定向,這說明瀏覽器須要再發一個新的請求。
- 200:請求成功,瀏覽器會把響應體內容(一般是html)顯示在瀏覽器中;
- 404:請求的資源沒有找到,說明客戶端錯誤的請求了不存在的資源;
- 500:請求資源找到了,但服務器內部出現了錯誤;
- 302:重定向,當響應碼爲302時,表示服務器要求瀏覽器從新再發一個請求,服務器會發送一個響應頭Location,它指定了新請求的URL地址;也能夠理解爲有緩存
- 304:
當用戶第一次請求index.html時,服務器會添加一個名爲Last-Modified響應頭,這個頭說明了 index.html的最後修改時間,瀏覽器會把index.html內容,以及最後響應時間緩存下來。當用戶第 二次請求index.html時,在請求中包含一個名爲If-Modified-Since請求頭,它的值就是第一次請 求時服務器經過Last-Modified響應頭髮送給瀏覽器的值,即index.html最後的修改時間, If-Modified-Since請求頭就是在告訴服務器,我這裏瀏覽器緩存的index.html最後修改時間是這個, 您看看如今的index.html最後修改時間是否是這個,若是仍是,那麼您就不用再響應這個index.html 內容了,我會把緩存的內容直接顯示出來。而服務器端會獲取If-Modified-Since值,與index.html 的當前最後修改時間比對,若是相同,服務器會發響應碼304,表示index.html與瀏覽器上次緩存的相 同,無需再次發送,瀏覽器能夠顯示本身的緩存頁面,若是比對不一樣,那麼說明index.html已經作了修 改,服務器會響應200。
B、其它響應頭
告訴瀏覽器不要緩存的響應頭:
- Expires: -1;
- Cache-Control: no-cache;
- Pragma: no-cache;
自動刷新響應頭,瀏覽器會在3秒以後請求http://www.baidu.com:
- Refresh: 3;url=http://www.baidu.com
C、HTML中指定響應頭
在HTMl頁面中可使用<meta http-equiv="" content="">來指定響應頭,例如在index.html頁面中給出<meta http-equiv="Refresh" content="3;url=http://www.baidu.com">,表示瀏覽器只會顯示index.html頁面3秒,而後自動跳轉到http://www.baidu.com.