python 全栈开发,Day66(web应用,http协议简介,web框架)
一、web應(yīng)用
web應(yīng)用程序是一種可以通過(guò)Web訪問(wèn)的應(yīng)用程序,程序的最大好處是用戶很容易訪問(wèn)應(yīng)用程序,用戶只需要有瀏覽器即可,不需要再安裝其他軟件。應(yīng)用程序有兩種模式C/S、B/S。C/S是客戶端/服務(wù)器端程序,也就是說(shuō)這類程序一般獨(dú)立運(yùn)行。而B/S就是瀏覽器端/服務(wù)器端應(yīng)用程序,這類應(yīng)用程序一般借助谷歌,火狐等瀏覽器來(lái)運(yùn)行。WEB應(yīng)用程序一般是B/S模式。Web應(yīng)用程序首先是“應(yīng)用程序”,和用標(biāo)準(zhǔn)的程序語(yǔ)言,如java,python等編寫出來(lái)的程序沒有什么本質(zhì)上的不同。在網(wǎng)絡(luò)編程的意義下,瀏覽器是一個(gè)socket客戶端,服務(wù)器是一個(gè)socket服務(wù)端。
?
B/S架構(gòu),是瀏覽器先發(fā)送請(qǐng)求,服務(wù)器響應(yīng)請(qǐng)求,返回?cái)?shù)據(jù)給客戶端。
簡(jiǎn)單socket服務(wù)器
舉例:
新建server.py文件,代碼如下:
import socket sk = socket.socket() sk.bind(('127.0.0.1',8800)) sk.listen()while True:print('server waiting...')conn,addr = sk.accept()# 服務(wù)器首先是接收數(shù)據(jù)data = conn.recv(1024)# 打印接收信息print('data',data)# 發(fā)送給客戶端conn.send(b'Hi,JD')conn.close() sk.close() View Code啟動(dòng)py文件,頁(yè)面訪問(wèn)url:
http://127.0.0.1:8800/
網(wǎng)頁(yè)輸出:
上面提示無(wú)效的響應(yīng),為什么?是因?yàn)榉?wù)器響應(yīng)信息,不符合HTTP規(guī)范。
查看pycharm控制臺(tái)的輸出信息
data b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8800\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC\r\n\r\n' View Code它才是一個(gè)完整的HTTP請(qǐng)求信息。
?
更改socket代碼,將conn.send改成下面的
conn.send(b'HTTP://1.1 200 OK\r\n\r\nHi,JD')重啟py文件,再次訪問(wèn)頁(yè)面
頁(yè)面輸出:?Hi,JD
?
修飾返回信息
響應(yīng)的信息,可以加入一些html標(biāo)簽,比如H1和img
import socket sk = socket.socket() sk.bind(('127.0.0.1',8800)) sk.listen()while True:print('server waiting...')conn,addr = sk.accept()# 服務(wù)器首先是接收數(shù)據(jù)data = conn.recv(1024)# 打印接收信息print('data',data)# 發(fā)送給客戶端html=b'<h1>Hi,JD</h1><img src="https://img20.360buyimg.com/da/jfs/t24334/1/45221916/115081/515da78a/5b2393d4N05f8a4c2.gif?t=1529495461508"/>'conn.send(b'HTTP://1.1 200 OK\r\n\r\n%s'%html)conn.close() sk.close() View Code重啟py文件,再次訪問(wèn)頁(yè)面,效果如下:
但是用字符串拼接,太麻煩了。
?
登錄頁(yè)面
可以引入一個(gè)index.html文件,來(lái)展示頁(yè)面
新建文件index.html,代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <form action=""><lable>用戶名</lable><input type="text"><lable>密碼</lable><input type="text"><input type="submit"> </form> </body> </html> View Code修改server.py,代碼如下:
import socket sk = socket.socket() sk.bind(('127.0.0.1',8800)) sk.listen()while True:print('server waiting...')conn,addr = sk.accept()# 服務(wù)器首先是接收數(shù)據(jù)data = conn.recv(1024)# 打印接收信息print('data',data)# 發(fā)送給客戶端with open("index.html","rb") as f: #必須使用rb模式打開data = f.read() # 讀取所有內(nèi)容conn.send(b'HTTP://1.1 200 OK\r\n\r\n%s'%data)conn.close() sk.close() View Code重啟py文件,再次訪問(wèn)頁(yè)面,效果如下:
?
二、http協(xié)議簡(jiǎn)介
http協(xié)議簡(jiǎn)介
HTTP協(xié)議是Hyper Text Transfer Protocol(超文本傳輸協(xié)議)的縮寫,是用于萬(wàn)維網(wǎng)(WWW:World Wide Web )服務(wù)器與本地瀏覽器之間傳輸超文本的傳送協(xié)議。
HTTP是一個(gè)屬于應(yīng)用層的面向?qū)ο蟮膮f(xié)議,由于其簡(jiǎn)捷、快速的方式,適用于分布式超媒體信息系統(tǒng)。它于1990年提出,經(jīng)過(guò)幾年的使用與發(fā)展,得到不斷地完善和擴(kuò)展。HTTP協(xié)議工作于客戶端-服務(wù)端架構(gòu)為上。瀏覽器作為HTTP客戶端通過(guò)URL向HTTP服務(wù)端即WEB服務(wù)器發(fā)送所有請(qǐng)求。Web服務(wù)器根據(jù)接收到的請(qǐng)求后,向客戶端發(fā)送響應(yīng)信息。
?
?
http協(xié)議特性
(1) 基于TCP/IP
http協(xié)議是基于TCP/IP協(xié)議之上的應(yīng)用層協(xié)議。
請(qǐng)求協(xié)議(瀏覽器-->服務(wù)器)
響應(yīng)協(xié)議(服務(wù)器-->瀏覽器)
比如:張三要發(fā)送一段信息為李四。發(fā)送的信息為
s = "zhangsan--24--shanghai"那么李四接收的時(shí)候,必須用--切割才能得到信息。否則李四不知道,這段信息是干啥的。這個(gè)是一個(gè)簡(jiǎn)單的基于內(nèi)容的協(xié)議。
對(duì)于HTTP而言,服務(wù)器和瀏覽器雙方遵循了共同的協(xié)議。
沒有請(qǐng)求,就沒有響應(yīng)
?
(2) 基于請(qǐng)求-響應(yīng)模式
HTTP協(xié)議規(guī)定,請(qǐng)求從客戶端發(fā)出,最后服務(wù)器端響應(yīng)該請(qǐng)求并 返回。換句話說(shuō),肯定是先從客戶端開始建立通信的,服務(wù)器端在沒有 接收到請(qǐng)求之前不會(huì)發(fā)送響應(yīng)
?
?
(3) 無(wú)狀態(tài)保存
HTTP是一種不保存狀態(tài),即無(wú)狀態(tài)(stateless)協(xié)議。HTTP協(xié)議 自身不對(duì)請(qǐng)求和響應(yīng)之間的通信狀態(tài)進(jìn)行保存。也就是說(shuō)在HTTP這個(gè) 級(jí)別,協(xié)議對(duì)于發(fā)送過(guò)的請(qǐng)求或響應(yīng)都不做持久化處理。
?
使用HTTP協(xié)議,每當(dāng)有新的請(qǐng)求發(fā)送時(shí),就會(huì)有對(duì)應(yīng)的新響應(yīng)產(chǎn) 生。協(xié)議本身并不保留之前一切的請(qǐng)求或響應(yīng)報(bào)文的信息。這是為了更快地處理大量事務(wù),確保協(xié)議的可伸縮性,而特意把HTTP協(xié)議設(shè)計(jì)成 如此簡(jiǎn)單的。可是,隨著Web的不斷發(fā)展,因無(wú)狀態(tài)而導(dǎo)致業(yè)務(wù)處理變得棘手 的情況增多了。比如,用戶登錄到一家購(gòu)物網(wǎng)站,即使他跳轉(zhuǎn)到該站的 其他頁(yè)面后,也需要能繼續(xù)保持登錄狀態(tài)。針對(duì)這個(gè)實(shí)例,網(wǎng)站為了能 夠掌握是誰(shuí)送出的請(qǐng)求,需要保存用戶的狀態(tài)。HTTP/1.1雖然是無(wú)狀態(tài)協(xié)議,但為了實(shí)現(xiàn)期望的保持狀態(tài)功能, 于是引入了Cookie技術(shù)。有了Cookie再用HTTP協(xié)議通信,就可以管 理狀態(tài)了。有關(guān)Cookie的詳細(xì)內(nèi)容稍后講解。
?
無(wú)連接
無(wú)連接的含義是限制每次連接只處理一個(gè)請(qǐng)求。服務(wù)器處理完客戶的請(qǐng)求,并收到客戶的應(yīng)答后,即斷開連接。采用這種方式可以節(jié)省傳輸時(shí)間。
比如訪問(wèn)jd網(wǎng)頁(yè),服務(wù)器響應(yīng)請(qǐng)求,返回html代碼給瀏覽器。瀏覽器接收后,連接就斷開了。
擴(kuò)展:還有一個(gè)短連接,比如請(qǐng)求響應(yīng)之后,維持3秒。如果客戶端沒有操作,連接就斷開了。
?
http請(qǐng)求協(xié)議與響應(yīng)協(xié)議
http協(xié)議包含由瀏覽器發(fā)送數(shù)據(jù)到服務(wù)器需要遵循的請(qǐng)求協(xié)議與服務(wù)器發(fā)送數(shù)據(jù)到瀏覽器需要遵循的請(qǐng)求協(xié)議。用于HTTP協(xié)議交互的信被為HTTP報(bào)文。請(qǐng)求端(客戶端)的HTTP報(bào)文 做請(qǐng)求報(bào)文,響應(yīng)端(服務(wù)器端)的 做響應(yīng)報(bào)文。HTTP報(bào)文本身是由多行數(shù)據(jù)構(gòu)成的字 文本。
?
請(qǐng)求協(xié)議
請(qǐng)求格式
?
注意:name=ueno&age=37 上面有一個(gè)空行。
Host和Conten-Length 之間的內(nèi)容屬于請(qǐng)求體,它是用來(lái)解釋本次請(qǐng)求的信息。
?
請(qǐng)求方式: get與post請(qǐng)求
- GET提交的數(shù)據(jù)會(huì)放在URL之后,以?分割URL和傳輸數(shù)據(jù),參數(shù)之間以&相連,如EditBook?name=test1&id=123456. POST方法是把提交的數(shù)據(jù)放在HTTP包的請(qǐng)求體中.
- GET提交的數(shù)據(jù)大小有限制(因?yàn)闉g覽器對(duì)URL的長(zhǎng)度有限制),而POST方法提交的數(shù)據(jù)沒有限制.
- GET與POST請(qǐng)求在服務(wù)端獲取請(qǐng)求數(shù)據(jù)方式不同。
響應(yīng)協(xié)議
響應(yīng)格式
請(qǐng)求協(xié)議(瀏覽器-->服務(wù)器)
" 請(qǐng)求首行:請(qǐng)求協(xié)議 url 請(qǐng)求方式 請(qǐng)求頭: 它是key:value形式的數(shù)據(jù)請(qǐng)求體...(注意:請(qǐng)求體和請(qǐng)求頭,必須有一個(gè)空行,也就是/r/n) "看下面2個(gè)url
https://passport.jd.com/new/login.aspx?ReturnUrl=http%3A%2F%2Fhome.jd.com%2Fhttp://127.0.0.1:8000/books/113/?age=18第一個(gè)url使用了域名,它涉及到一個(gè)dns解析過(guò)程。域名后面沒有端口,表示使用默認(rèn)端口。https端口為443
第二個(gè)url,端口之后和問(wèn)號(hào)之間的部分,叫做路徑。不管多少層,只要沒遇到問(wèn)號(hào),都屬于路徑部分。
問(wèn)號(hào)之后的部分,叫做數(shù)據(jù)。
看這個(gè)url: https://www.jd.com/? ?它的路徑就是 / 。/表示根路徑
所以一個(gè)完整的url由4部分組成:協(xié)議、域名/IP和端口、路徑、數(shù)據(jù)
?
查看Pycharm控制臺(tái),使用谷歌瀏覽器訪問(wèn)一次網(wǎng)頁(yè)。實(shí)際上,是有2次請(qǐng)求的。
data b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8800\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC\r\n\r\n'server waiting...data b'GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8800\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36\r\nAccept: image/webp,image/apng,image/*,*/*;q=0.8\r\nReferer: http://127.0.0.1:8800/\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC\r\n\r\n' View Code第一次,是正常請(qǐng)求。第二次是,favicon.ico請(qǐng)求,它是網(wǎng)頁(yè)圖標(biāo)問(wèn)題。這個(gè)請(qǐng)求,忽略即可。
將/r/n替換為換行,得到以下信息
GET / HTTP/1.1 Host: 127.0.0.1:8800 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC View Code可以看到,GET的數(shù)據(jù),是放到url后面的。POST數(shù)據(jù)是放在請(qǐng)求體后面的。
打個(gè)比方:比如早期時(shí)候,用的信封。get相當(dāng)于,直接寫在封面上了。post相當(dāng)于寫在信封里面了。
?
舉例:
使用表單模擬post請(qǐng)求
更改index.html,代碼如下:
注意:action不能和當(dāng)前網(wǎng)頁(yè)路徑一樣,比如http://127.0.0.1:8800,否則提交之后,頁(yè)面會(huì)卡死。
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <form action="/abc" method="post"><lable>用戶名</lable><input type="text" name="user"/><lable>密碼</lable><input type="password" name="pwd"/><input type="submit"/> </form> </body> </html> View Code重啟server.py文件,訪問(wèn)頁(yè)面
輸入用戶名和密碼,點(diǎn)擊提交
查看pycharm的控制臺(tái),查看post請(qǐng)求
data b'POST /abc HTTP/1.1\r\nHost: 127.0.0.1:8800\r\nConnection: keep-alive\r\nContent-Length: 17\r\nCache-Control: max-age=0\r\nOrigin: http://127.0.0.1:8800\r\nUpgrade-Insecure-Requests: 1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nReferer: http://127.0.0.1:8800/abc?user=xiao&pwd=123\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC\r\n\r\nuser=xiao&pwd=123' View Code看最后的信息,就可以看到user=xiao&pwd=123'
?
模擬get請(qǐng)求
修改index.html,代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <form action="" method="get"><lable>用戶名</lable><input type="text" name="user"/><lable>密碼</lable><input type="password" name="pwd"/><input type="submit"/> </form> </body> </html> View Code輸入用戶名和密碼,點(diǎn)擊提交
發(fā)現(xiàn)url就發(fā)生變化了,數(shù)據(jù)保存到url中
?
?響應(yīng)協(xié)議(服務(wù)器-->瀏覽器)
響應(yīng)首行: 請(qǐng)求協(xié)議 協(xié)議碼 OK 響應(yīng)頭:key:value響應(yīng)體注意:響應(yīng)體和響應(yīng)頭有一個(gè)空行。
響應(yīng)頭,可要可不要。比如上面的socket,響應(yīng)信息,就沒有響應(yīng)頭。
響應(yīng)體,是瀏覽器真正加載的內(nèi)容。
使用谷歌瀏覽器打開網(wǎng)頁(yè),按f12打開控制臺(tái)-->networkd-->點(diǎn)擊左邊的連接-->Respone,這里面就是響應(yīng)體
?
瀏覽器發(fā)送也是一堆字符串
瀏覽器從服務(wù)器得到響應(yīng)信息,也是拿到一堆字符串
?
增加一個(gè)響應(yīng)頭,比如Content-Type: text/html
修改server.py,代碼如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- import socket sk = socket.socket() sk.bind(('127.0.0.1',8800)) sk.listen(5)while True:print('server waiting...')conn,addr = sk.accept()# 服務(wù)器首先是接收數(shù)據(jù)data = conn.recv(1024)# 打印接收信息print('data',data)# 發(fā)送給客戶端with open("index.html","rb") as f: #必須使用rb模式打開data = f.read() # 讀取所有內(nèi)容conn.send(b'HTTP://1.1 200 OK\r\nContent-Type: text/html\r\n\r\n%s'%data)conn.close() sk.close() View Code重啟socket.py,打開控制臺(tái),查看網(wǎng)絡(luò)。訪問(wèn)網(wǎng)頁(yè)http://127.0.0.1:8800/
發(fā)現(xiàn)多了一個(gè)響應(yīng)頭
請(qǐng)求頭和響應(yīng)頭,都是很有意義的
請(qǐng)求頭有啥用呢?
比如這個(gè):
爬蟲應(yīng)用,如果沒有帶user-agent。那么服務(wù)器,就拒絕請(qǐng)求。
響應(yīng)式頁(yè)面,也是通過(guò)user-agent來(lái)判斷的
?
響應(yīng)狀態(tài)碼
狀態(tài)碼的職 是當(dāng)客戶端向服務(wù)器端發(fā)送請(qǐng)求時(shí), 返回的請(qǐng)求 結(jié)果。借助狀態(tài)碼,用戶可以知道服務(wù)器端是正常 理了請(qǐng)求,還是出 現(xiàn)了 。狀態(tài)碼如200 OK,以3位數(shù)字和原因 成。數(shù)字中的 一位指定了響應(yīng) 別,后兩位無(wú)分 。響應(yīng) 別有以5種。
?以后會(huì)大量用到3xx狀態(tài)
301 永久性重定向
永久性重定向。該狀態(tài)碼表示請(qǐng)求的資源已被分配了新的UR1,以后應(yīng)使用資源現(xiàn)在所指的URI。也就是說(shuō),如果已經(jīng)把資源對(duì)應(yīng)的UR1保存為書簽了,這時(shí)應(yīng)該按Location首部字段提示的UR1重新保存。
像下方給出的請(qǐng)求URI,當(dāng)指定資源路徑的最后忘記添加斜杠"/",就會(huì)產(chǎn)生301狀態(tài)碼。
301使用2次請(qǐng)求。 一次是初始請(qǐng)求,第二次是訪問(wèn)新的鏈接。
?
302臨時(shí)性重定向
?
?
臨時(shí)性重定向。該狀態(tài)碼表示請(qǐng)求的資源已被分配了新的URI,希望用戶(本次)能使用新的URI訪問(wèn)。
和301MovedPermanently狀態(tài)碼相似,但302狀態(tài)碼代表的資源不是被永久移動(dòng),只是臨時(shí)性質(zhì)的。換句話說(shuō),已移動(dòng)的資源對(duì)應(yīng)的URI將來(lái)還有可能發(fā)生改變。比如,用戶把UR丨保存成書簽,但不會(huì)像301狀態(tài)碼出現(xiàn)時(shí)那樣去更新書簽,而是仍舊保留返回302狀態(tài)碼的頁(yè)面對(duì)應(yīng)的UR1。
?
注意:面試會(huì)問(wèn)道301和302的區(qū)別
《HTTP圖解》這本書可以看一下
還有一本,《HTTP權(quán)威指南》這個(gè)太復(fù)雜了,目前可以不看。
三、web框架
Web框架
Web框架(Web framework)是一種開發(fā)框架,用來(lái)支持動(dòng)態(tài)網(wǎng)站、網(wǎng)絡(luò)應(yīng)用和網(wǎng)絡(luò)服務(wù)的開發(fā)。這大多數(shù)的web框架提供了一套開發(fā)和部署網(wǎng)站的方式,也為web行為提供了一套通用的方法。web框架已經(jīng)實(shí)現(xiàn)了很多功能,開發(fā)人員使用框架提供的方法并且完成自己的業(yè)務(wù)邏輯,就能快速開發(fā)web應(yīng)用了。瀏覽器和服務(wù)器的是基于HTTP協(xié)議進(jìn)行通信的。也可以說(shuō)web框架就是在以上十幾行代碼基礎(chǔ)張擴(kuò)展出來(lái)的,有很多簡(jiǎn)單方便使用的方法,大大提高了開發(fā)的效率。
wsgiref模塊
最簡(jiǎn)單的Web應(yīng)用就是先把HTML用文件保存好,用一個(gè)現(xiàn)成的HTTP服務(wù)器軟件,接收用戶請(qǐng)求,從文件中讀取HTML,返回。
如果要?jiǎng)討B(tài)生成HTML,就需要把上述步驟自己來(lái)實(shí)現(xiàn)。不過(guò),接受HTTP請(qǐng)求、解析HTTP請(qǐng)求、發(fā)送HTTP響應(yīng)都是苦力活,如果我們自己來(lái)寫這些底層代碼,還沒開始寫動(dòng)態(tài)HTML呢,就得花個(gè)把月去讀HTTP規(guī)范。
正確的做法是底層代碼由專門的服務(wù)器軟件實(shí)現(xiàn),我們用Python專注于生成HTML文檔。因?yàn)槲覀儾幌M佑|到TCP連接、HTTP原始請(qǐng)求和響應(yīng)格式,所以,需要一個(gè)統(tǒng)一的接口協(xié)議來(lái)實(shí)現(xiàn)這樣的服務(wù)器軟件,讓我們專心用Python編寫Web業(yè)務(wù)。這個(gè)接口就是WSGI:Web Server Gateway Interface。而wsgiref模塊就是python基于wsgi協(xié)議開發(fā)的服務(wù)模塊。
?
由于url路徑在請(qǐng)求信息,里面有大量的字符串。比如下面的一段信息:
GET /index/ HTTP/1.1 Host: 127.0.0.1:8800 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC View Code要得到/index/,可以使用正則、split切割...等方式。但是這樣太麻煩了。
現(xiàn)在有一個(gè)內(nèi)置模塊wsgiref,它可以解析這些信息,并返回一個(gè)字典格式。那么就可以方便取數(shù)據(jù)了。
?
舉例:
新建文件wsgiref_start.py,內(nèi)容如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from wsgiref.simple_server import make_server#所有請(qǐng)求信息都在environ,它會(huì)傳給application def application(environ, start_response):print(environ) #打印environ信息start_response('200 OK', [('Content-Type', 'text/html')])return [b'<h1>Hello, web!</h1>']#不寫ip,默認(rèn)監(jiān)聽本機(jī)ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監(jiān)聽HTTP請(qǐng)求: httpd.serve_forever() View Code運(yùn)行py文件,訪問(wèn)頁(yè)面
如果訪問(wèn)頁(yè)面失敗,嘗試換一個(gè)端口,就可以了。
?
DIY一個(gè)web框架
訪問(wèn)首頁(yè),查看Pycharm控制臺(tái)輸出信息,這就是完整的environ信息,返回的是字典格式。
{'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'PROCESSOR_ARCHITECTURE': 'AMD64', 'HOMEPATH': '\\Users\\xiao', 'PATH': 'C:\\Python35\\Scripts\\;C:\\Python35\\;C:\\Program Files\\Python36\\Scripts\\;C:\\Program Files\\Python36\\;C:\\Program Files (x86)\\Common Files\\NetSarang;C:\\Program Files (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\Intel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;D:\\Program Files\\Git\\bin;C:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance Toolkit\\;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;D:\\Program Files (x86)\\ffmpeg-20180518-16b4f97-win64-shared\\bin;C:\\Program Files\\nodejs\\;D:\\Program Files (x86)\\mysql-5.7.22-winx64\\bin;C:\\Users\\xiao\\AppData\\Local\\Microsoft\\WindowsApps;D:\\Program Files\\Git\\bin;C:\\Users\\xiao\\AppData\\Roaming\\npm;C:\\Python35\\lib\\site-packages\\numpy\\.libs', 'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', 'COMSPEC': 'C:\\WINDOWS\\system32\\cmd.exe', 'HTTP_ACCEPT': 'image/webp,image/apng,image/*,*/*;q=0.8', 'PATH_INFO': '/favicon.ico', 'HTTP_REFERER': 'http://127.0.0.1:8888/', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'PYCHARM_MATPLOTLIB_PORT': '50111', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36', 'LOCALAPPDATA': 'C:\\Users\\xiao\\AppData\\Local', 'PROGRAMDATA': 'C:\\ProgramData', 'SERVER_PROTOCOL': 'HTTP/1.1', 'WINDIR': 'C:\\WINDOWS', 'VS140COMNTOOLS': 'C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\', 'ALLUSERSPROFILE': 'C:\\ProgramData', 'REQUEST_METHOD': 'GET', 'OS': 'Windows_NT', 'USERDOMAIN_ROAMINGPROFILE': 'DESKTOP-CFMVJ8G', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'REMOTE_HOST': '', 'SCRIPT_NAME': '', 'APPDATA': 'C:\\Users\\xiao\\AppData\\Roaming', 'HTTP_HOST': '127.0.0.1:8888', 'wsgi.multiprocess': False, 'GATEWAY_INTERFACE': 'CGI/1.1', 'wsgi.version': (1, 0), 'PROCESSOR_LEVEL': '6', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>, 'COMPUTERNAME': 'DESKTOP-CFMVJ8G', 'SESSIONNAME': 'Console', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'SYSTEMROOT': 'C:\\WINDOWS', 'HTTP_CONNECTION': 'keep-alive', 'TEMP': 'C:\\Users\\xiao\\AppData\\Local\\Temp', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW', 'SERVER_NAME': 'DESKTOP-CFMVJ8G', 'LOGONSERVER': '\\\\DESKTOP-CFMVJ8G', 'PROGRAMW6432': 'C:\\Program Files', 'HOMEDRIVE': 'C:', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'QUERY_STRING': '', 'PUBLIC': 'C:\\Users\\Public', 'PROCESSOR_REVISION': '5e03', 'USERNAME': 'xiao', 'wsgi.run_once': False, 'PYTHONPATH': 'C:\\Program Files\\JetBrains\\PyCharm 2018.1.1\\helpers\\pycharm_matplotlib_backend;E:\\python_script', 'VS90COMNTOOLS': 'D:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\Common7\\Tools\\', 'PYCHARM_HOSTED': '1', 'USERPROFILE': 'C:\\Users\\xiao', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 94 Stepping 3, GenuineIntel', 'TMP': 'C:\\Users\\xiao\\AppData\\Local\\Temp', 'PROGRAMFILES': 'C:\\Program Files', 'wsgi.multithread': True, 'USERDOMAIN': 'DESKTOP-CFMVJ8G', 'HTTP_COOKIE': 'csrftoken=IwhDDZ9RiKQUV4T5CbzGIhAcVZNxYuvAYdS7RKc0tmOmk02hHWfQ8sWnIGrN1pzC', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules', 'SERVER_PORT': '8888', 'PYTHONUNBUFFERED': '1', 'wsgi.input': <_io.BufferedReader name=340>, 'NUMBER_OF_PROCESSORS': '8', 'SYSTEMDRIVE': 'C:', 'CONTENT_LENGTH': '', 'wsgi.url_scheme': 'http', 'PYTHONIOENCODING': 'UTF-8'} View CodePATH_INFO就是請(qǐng)求路徑
上面信息太多了,只打印PATH_INFO,更改print(environ)為
print('path:',environ.get("PATH_INFO"))重啟py文件,再次訪問(wèn)頁(yè)面,查看pycharm控制臺(tái),輸出
path: / 127.0.0.1 - - [20/Jun/2018 23:22:33] "GET / HTTP/1.1" 200 20 127.0.0.1 - - [20/Jun/2018 23:22:33] "GET /favicon.ico HTTP/1.1" 200 20 path: /favicon.ico得到路徑,就可以根據(jù)路徑判斷,來(lái)渲染不同的html文件了
創(chuàng)建index.html,代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><h3>Index</h3></body> </html> View Code創(chuàng)建login.html,代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body> <form action="/abc" method="post"><lable>用戶名</lable><input type="text" name="user"/><lable>密碼</lable><input type="password" name="pwd"/><input type="submit"/> </form> </body> </html> View Code?編輯wsgiref_start.py文件,加入路徑判斷。
#!/usr/bin/env python # -*- coding: utf-8 -*- from wsgiref.simple_server import make_server#所有請(qǐng)求信息都在environ,它會(huì)傳給application def application(environ, start_response):# print('path:',environ.get("PATH_INFO"))path = environ.get("PATH_INFO")start_response('200 OK', [('Content-Type', 'text/html')])if path == '/login/': # 注意路徑后面,必須有/with open("login.html","rb") as f:data = f.read()return [data]elif path == '/index/':with open("index.html","rb") as f:data = f.read()return [data]else:return [b"<h1>404</h1>"]#不寫ip,默認(rèn)監(jiān)聽本機(jī)ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監(jiān)聽HTTP請(qǐng)求: httpd.serve_forever() View Code重啟py文件,訪問(wèn)以下url:
http://127.0.0.1:8888/index/
注意:index后面必須有一個(gè)/,否則輸出404
頁(yè)面輸出:
?
http://127.0.0.1:8888/login/
注意:login后面必須有一個(gè)/,否則輸出404
頁(yè)面輸出:
?
http://127.0.0.1:8888/abc/
訪問(wèn)不存在的,頁(yè)面輸出:
?
?
將url判斷和頁(yè)面輸出部分,封裝成函數(shù),wsgiref_start.py代碼如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from wsgiref.simple_server import make_server#視圖函數(shù),如果需要用到請(qǐng)求信息,必須要傳environ變量 def login(environ):with open("login.html", "rb") as f:data = f.read()return datadef index(environ):with open("index.html" , "rb") as f:data = f.read()return data#所有請(qǐng)求信息都在environ,它會(huì)傳給application def application(environ, start_response):#當(dāng)前訪問(wèn)路徑current_path = environ.get("PATH_INFO")#響應(yīng)給客戶端200狀態(tài)start_response('200 OK', [('Content-Type', 'text/html')])#url控制,匹配url時(shí),調(diào)用對(duì)應(yīng)的視圖函數(shù)urlpatterns = [("/login/", login),("/index/", index),]#初始變量func = None#遍歷url列表for item in urlpatterns:#當(dāng)列表的url和當(dāng)前訪問(wèn)路徑相同時(shí)if item[0] == current_path:#將視圖函數(shù)賦值給func,注意:這里并沒有執(zhí)行函數(shù)func = item[1]#這里必須要跳出循環(huán)break#判斷func變量不為Noneif func:ret = func(environ) # 執(zhí)行視圖函數(shù),必須傳入environreturn [ret] # 返回給瀏覽器else:return [b"<h1>404</h1>"] # 輸出404頁(yè)面#不寫ip,默認(rèn)監(jiān)聽本機(jī)ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監(jiān)聽HTTP請(qǐng)求: httpd.serve_forever() View Code重啟py文件,再次訪問(wèn)上面3個(gè)url。訪問(wèn)結(jié)果一致,說(shuō)明ok了。
?
拆分代碼
上面的代碼,邏輯部分都集中在一個(gè)py中,這樣不方便以后的擴(kuò)展。
比如有30個(gè)url,寫30個(gè)if判斷嗎?太low了。需要解耦
1. 分離url
新建urls.py文件,代碼如下:
import viewsurlpatterns=[("/login/",views.login),("/index/",views.index),] View Code2.分離視圖函數(shù)
新建views.py文件,代碼如下:
def login(environ):with open("templates/login.html", "rb") as f:data = f.read()return datadef index(environ):with open("templates/index.html" , "rb") as f:data = f.read()return data View Code3.分離模板文件,比如html文件
創(chuàng)建目錄templates,將index.html和login.html移動(dòng)到此目錄
4.修改wsgiref_start.py文件,代碼如下:
from wsgiref.simple_server import make_server from urls import urlpatterns # 導(dǎo)入自定義的urls模塊#所有請(qǐng)求信息都在environ,它會(huì)傳給application def application(environ, start_response):#當(dāng)前訪問(wèn)路徑current_path = environ.get("PATH_INFO")print(current_path)#響應(yīng)給客戶端200狀態(tài)start_response('200 OK', [('Content-Type', 'text/html')])#初始變量func = None#遍歷url列表for item in urlpatterns:#當(dāng)列表的url和當(dāng)前訪問(wèn)路徑相同時(shí)if item[0] == current_path:#將視圖函數(shù)賦值給func,注意:這里并沒有執(zhí)行函數(shù)func = item[1]#這里必須要跳出循環(huán)break#判斷func變量不為Noneif func:ret = func(environ) # 執(zhí)行視圖函數(shù),必須傳入environreturn [ret] # 返回給瀏覽器else:return [b"<h1>404</h1>"] # 輸出404頁(yè)面#不寫ip,默認(rèn)監(jiān)聽本機(jī)ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監(jiān)聽HTTP請(qǐng)求: httpd.serve_forever() View Code重啟pwsgiref_start.py文件,再次訪問(wèn)上面3個(gè)url。訪問(wèn)結(jié)果一致,說(shuō)明ok了。
到這里,一個(gè)簡(jiǎn)單web框架,就完成了!
將當(dāng)前文件夾打包,扔到別的電腦或者服務(wù)器,只有有python環(huán)境,就可以運(yùn)行了。
?
新整頁(yè)面
修改urls.py,增加一個(gè)路徑timer,用來(lái)顯示當(dāng)前時(shí)間
import viewsurlpatterns=[("/login/",views.login),("/index/",views.index),("/timer/",views.timer) ] View Code修改views.py,增加一個(gè)視圖函數(shù)
import datetimedef login(environ):with open("templates/login.html", "rb") as f:data = f.read()return datadef index(environ):with open("templates/index.html" , "rb") as f:data = f.read()return datadef timer(environ): # 返回當(dāng)前時(shí)間# 獲得當(dāng)前時(shí)間now = datetime.datetime.now()# 轉(zhuǎn)換為指定的格式:otherStyleTime = now.strftime("%Y-%m-%d %H:%M:%S")return otherStyleTime.encode('utf-8') # 必須為bytes類型 View Code重啟pwsgiref_start.py文件,訪問(wèn)url:
http://127.0.0.1:8888/timer/
頁(yè)面輸出:
?
參考資料:
Django 基礎(chǔ)教程
轉(zhuǎn)載聲明:
作者:肖祥
出處:?https://www.cnblogs.com/xiao987334176/
轉(zhuǎn)載于:https://www.cnblogs.com/bqwzx/p/10177401.html
總結(jié)
以上是生活随笔為你收集整理的python 全栈开发,Day66(web应用,http协议简介,web框架)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 校内oj??!
- 下一篇: 2019做的第一个艰难决定