python 全栈开发,Day66(web应用,http协议简介,web框架)
一、web應用
web應用程序是一種可以通過Web訪問的應用程序,程序的最大好處是用戶很容易訪問應用程序,用戶只需要有瀏覽器即可,不需要再安裝其他軟件。應用程序有兩種模式C/S、B/S。C/S是客戶端/服務器端程序,也就是說這類程序一般獨立運行。而B/S就是瀏覽器端/服務器端應用程序,這類應用程序一般借助谷歌,火狐等瀏覽器來運行。WEB應用程序一般是B/S模式。Web應用程序首先是“應用程序”,和用標準的程序語言,如java,python等編寫出來的程序沒有什么本質上的不同。在網絡編程的意義下,瀏覽器是一個socket客戶端,服務器是一個socket服務端。
?
B/S架構,是瀏覽器先發送請求,服務器響應請求,返回數據給客戶端。
簡單socket服務器
舉例:
新建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()# 服務器首先是接收數據data = conn.recv(1024)# 打印接收信息print('data',data)# 發送給客戶端conn.send(b'Hi,JD')conn.close() sk.close() View Code啟動py文件,頁面訪問url:
http://127.0.0.1:8800/
網頁輸出:
上面提示無效的響應,為什么?是因為服務器響應信息,不符合HTTP規范。
查看pycharm控制臺的輸出信息
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它才是一個完整的HTTP請求信息。
?
更改socket代碼,將conn.send改成下面的
conn.send(b'HTTP://1.1 200 OK\r\n\r\nHi,JD')重啟py文件,再次訪問頁面
頁面輸出:?Hi,JD
?
修飾返回信息
響應的信息,可以加入一些html標簽,比如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()# 服務器首先是接收數據data = conn.recv(1024)# 打印接收信息print('data',data)# 發送給客戶端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文件,再次訪問頁面,效果如下:
但是用字符串拼接,太麻煩了。
?
登錄頁面
可以引入一個index.html文件,來展示頁面
新建文件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()# 服務器首先是接收數據data = conn.recv(1024)# 打印接收信息print('data',data)# 發送給客戶端with open("index.html","rb") as f: #必須使用rb模式打開data = f.read() # 讀取所有內容conn.send(b'HTTP://1.1 200 OK\r\n\r\n%s'%data)conn.close() sk.close() View Code重啟py文件,再次訪問頁面,效果如下:
?
二、http協議簡介
http協議簡介
HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用于萬維網(WWW:World Wide Web )服務器與本地瀏覽器之間傳輸超文本的傳送協議。
HTTP是一個屬于應用層的面向對象的協議,由于其簡捷、快速的方式,適用于分布式超媒體信息系統。它于1990年提出,經過幾年的使用與發展,得到不斷地完善和擴展。HTTP協議工作于客戶端-服務端架構為上。瀏覽器作為HTTP客戶端通過URL向HTTP服務端即WEB服務器發送所有請求。Web服務器根據接收到的請求后,向客戶端發送響應信息。
?
?
http協議特性
(1) 基于TCP/IP
http協議是基于TCP/IP協議之上的應用層協議。
請求協議(瀏覽器-->服務器)
響應協議(服務器-->瀏覽器)
比如:張三要發送一段信息為李四。發送的信息為
s = "zhangsan--24--shanghai"那么李四接收的時候,必須用--切割才能得到信息。否則李四不知道,這段信息是干啥的。這個是一個簡單的基于內容的協議。
對于HTTP而言,服務器和瀏覽器雙方遵循了共同的協議。
沒有請求,就沒有響應
?
(2) 基于請求-響應模式
HTTP協議規定,請求從客戶端發出,最后服務器端響應該請求并 返回。換句話說,肯定是先從客戶端開始建立通信的,服務器端在沒有 接收到請求之前不會發送響應
?
?
(3) 無狀態保存
HTTP是一種不保存狀態,即無狀態(stateless)協議。HTTP協議 自身不對請求和響應之間的通信狀態進行保存。也就是說在HTTP這個 級別,協議對于發送過的請求或響應都不做持久化處理。
?
使用HTTP協議,每當有新的請求發送時,就會有對應的新響應產 生。協議本身并不保留之前一切的請求或響應報文的信息。這是為了更快地處理大量事務,確保協議的可伸縮性,而特意把HTTP協議設計成 如此簡單的??墒?隨著Web的不斷發展,因無狀態而導致業務處理變得棘手 的情況增多了。比如,用戶登錄到一家購物網站,即使他跳轉到該站的 其他頁面后,也需要能繼續保持登錄狀態。針對這個實例,網站為了能 夠掌握是誰送出的請求,需要保存用戶的狀態。HTTP/1.1雖然是無狀態協議,但為了實現期望的保持狀態功能, 于是引入了Cookie技術。有了Cookie再用HTTP協議通信,就可以管 理狀態了。有關Cookie的詳細內容稍后講解。
?
無連接
無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,并收到客戶的應答后,即斷開連接。采用這種方式可以節省傳輸時間。
比如訪問jd網頁,服務器響應請求,返回html代碼給瀏覽器。瀏覽器接收后,連接就斷開了。
擴展:還有一個短連接,比如請求響應之后,維持3秒。如果客戶端沒有操作,連接就斷開了。
?
http請求協議與響應協議
http協議包含由瀏覽器發送數據到服務器需要遵循的請求協議與服務器發送數據到瀏覽器需要遵循的請求協議。用于HTTP協議交互的信被為HTTP報文。請求端(客戶端)的HTTP報文 做請求報文,響應端(服務器端)的 做響應報文。HTTP報文本身是由多行數據構成的字 文本。
?
請求協議
請求格式
?
注意:name=ueno&age=37 上面有一個空行。
Host和Conten-Length 之間的內容屬于請求體,它是用來解釋本次請求的信息。
?
請求方式: get與post請求
- GET提交的數據會放在URL之后,以?分割URL和傳輸數據,參數之間以&相連,如EditBook?name=test1&id=123456. POST方法是把提交的數據放在HTTP包的請求體中.
- GET提交的數據大小有限制(因為瀏覽器對URL的長度有限制),而POST方法提交的數據沒有限制.
- GET與POST請求在服務端獲取請求數據方式不同。
響應協議
響應格式
請求協議(瀏覽器-->服務器)
" 請求首行:請求協議 url 請求方式 請求頭: 它是key:value形式的數據請求體...(注意:請求體和請求頭,必須有一個空行,也就是/r/n) "看下面2個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第一個url使用了域名,它涉及到一個dns解析過程。域名后面沒有端口,表示使用默認端口。https端口為443
第二個url,端口之后和問號之間的部分,叫做路徑。不管多少層,只要沒遇到問號,都屬于路徑部分。
問號之后的部分,叫做數據。
看這個url: https://www.jd.com/? ?它的路徑就是 / 。/表示根路徑
所以一個完整的url由4部分組成:協議、域名/IP和端口、路徑、數據
?
查看Pycharm控制臺,使用谷歌瀏覽器訪問一次網頁。實際上,是有2次請求的。
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第一次,是正常請求。第二次是,favicon.ico請求,它是網頁圖標問題。這個請求,忽略即可。
將/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的數據,是放到url后面的。POST數據是放在請求體后面的。
打個比方:比如早期時候,用的信封。get相當于,直接寫在封面上了。post相當于寫在信封里面了。
?
舉例:
使用表單模擬post請求
更改index.html,代碼如下:
注意:action不能和當前網頁路徑一樣,比如http://127.0.0.1:8800,否則提交之后,頁面會卡死。
<!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文件,訪問頁面
輸入用戶名和密碼,點擊提交
查看pycharm的控制臺,查看post請求
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請求
修改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輸入用戶名和密碼,點擊提交
發現url就發生變化了,數據保存到url中
?
?響應協議(服務器-->瀏覽器)
響應首行: 請求協議 協議碼 OK 響應頭:key:value響應體注意:響應體和響應頭有一個空行。
響應頭,可要可不要。比如上面的socket,響應信息,就沒有響應頭。
響應體,是瀏覽器真正加載的內容。
使用谷歌瀏覽器打開網頁,按f12打開控制臺-->networkd-->點擊左邊的連接-->Respone,這里面就是響應體
?
瀏覽器發送也是一堆字符串
瀏覽器從服務器得到響應信息,也是拿到一堆字符串
?
增加一個響應頭,比如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()# 服務器首先是接收數據data = conn.recv(1024)# 打印接收信息print('data',data)# 發送給客戶端with open("index.html","rb") as f: #必須使用rb模式打開data = f.read() # 讀取所有內容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,打開控制臺,查看網絡。訪問網頁http://127.0.0.1:8800/
發現多了一個響應頭
請求頭和響應頭,都是很有意義的
請求頭有啥用呢?
比如這個:
爬蟲應用,如果沒有帶user-agent。那么服務器,就拒絕請求。
響應式頁面,也是通過user-agent來判斷的
?
響應狀態碼
狀態碼的職 是當客戶端向服務器端發送請求時, 返回的請求 結果。借助狀態碼,用戶可以知道服務器端是正常 理了請求,還是出 現了 。狀態碼如200 OK,以3位數字和原因 成。數字中的 一位指定了響應 別,后兩位無分 。響應 別有以5種。
?以后會大量用到3xx狀態
301 永久性重定向
永久性重定向。該狀態碼表示請求的資源已被分配了新的UR1,以后應使用資源現在所指的URI。也就是說,如果已經把資源對應的UR1保存為書簽了,這時應該按Location首部字段提示的UR1重新保存。
像下方給出的請求URI,當指定資源路徑的最后忘記添加斜杠"/",就會產生301狀態碼。
301使用2次請求。 一次是初始請求,第二次是訪問新的鏈接。
?
302臨時性重定向
?
?
臨時性重定向。該狀態碼表示請求的資源已被分配了新的URI,希望用戶(本次)能使用新的URI訪問。
和301MovedPermanently狀態碼相似,但302狀態碼代表的資源不是被永久移動,只是臨時性質的。換句話說,已移動的資源對應的URI將來還有可能發生改變。比如,用戶把UR丨保存成書簽,但不會像301狀態碼出現時那樣去更新書簽,而是仍舊保留返回302狀態碼的頁面對應的UR1。
?
注意:面試會問道301和302的區別
《HTTP圖解》這本書可以看一下
還有一本,《HTTP權威指南》這個太復雜了,目前可以不看。
三、web框架
Web框架
Web框架(Web framework)是一種開發框架,用來支持動態網站、網絡應用和網絡服務的開發。這大多數的web框架提供了一套開發和部署網站的方式,也為web行為提供了一套通用的方法。web框架已經實現了很多功能,開發人員使用框架提供的方法并且完成自己的業務邏輯,就能快速開發web應用了。瀏覽器和服務器的是基于HTTP協議進行通信的。也可以說web框架就是在以上十幾行代碼基礎張擴展出來的,有很多簡單方便使用的方法,大大提高了開發的效率。
wsgiref模塊
最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。
如果要動態生成HTML,就需要把上述步驟自己來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,如果我們自己來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規范。
正確的做法是底層代碼由專門的服務器軟件實現,我們用Python專注于生成HTML文檔。因為我們不希望接觸到TCP連接、HTTP原始請求和響應格式,所以,需要一個統一的接口協議來實現這樣的服務器軟件,讓我們專心用Python編寫Web業務。這個接口就是WSGI:Web Server Gateway Interface。而wsgiref模塊就是python基于wsgi協議開發的服務模塊。
?
由于url路徑在請求信息,里面有大量的字符串。比如下面的一段信息:
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切割...等方式。但是這樣太麻煩了。
現在有一個內置模塊wsgiref,它可以解析這些信息,并返回一個字典格式。那么就可以方便取數據了。
?
舉例:
新建文件wsgiref_start.py,內容如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from wsgiref.simple_server import make_server#所有請求信息都在environ,它會傳給application def application(environ, start_response):print(environ) #打印environ信息start_response('200 OK', [('Content-Type', 'text/html')])return [b'<h1>Hello, web!</h1>']#不寫ip,默認監聽本機ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監聽HTTP請求: httpd.serve_forever() View Code運行py文件,訪問頁面
如果訪問頁面失敗,嘗試換一個端口,就可以了。
?
DIY一個web框架
訪問首頁,查看Pycharm控制臺輸出信息,這就是完整的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就是請求路徑
上面信息太多了,只打印PATH_INFO,更改print(environ)為
print('path:',environ.get("PATH_INFO"))重啟py文件,再次訪問頁面,查看pycharm控制臺,輸出
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得到路徑,就可以根據路徑判斷,來渲染不同的html文件了
創建index.html,代碼如下:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><h3>Index</h3></body> </html> View Code創建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#所有請求信息都在environ,它會傳給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,默認監聽本機ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監聽HTTP請求: httpd.serve_forever() View Code重啟py文件,訪問以下url:
http://127.0.0.1:8888/index/
注意:index后面必須有一個/,否則輸出404
頁面輸出:
?
http://127.0.0.1:8888/login/
注意:login后面必須有一個/,否則輸出404
頁面輸出:
?
http://127.0.0.1:8888/abc/
訪問不存在的,頁面輸出:
?
?
將url判斷和頁面輸出部分,封裝成函數,wsgiref_start.py代碼如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from wsgiref.simple_server import make_server#視圖函數,如果需要用到請求信息,必須要傳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#所有請求信息都在environ,它會傳給application def application(environ, start_response):#當前訪問路徑current_path = environ.get("PATH_INFO")#響應給客戶端200狀態start_response('200 OK', [('Content-Type', 'text/html')])#url控制,匹配url時,調用對應的視圖函數urlpatterns = [("/login/", login),("/index/", index),]#初始變量func = None#遍歷url列表for item in urlpatterns:#當列表的url和當前訪問路徑相同時if item[0] == current_path:#將視圖函數賦值給func,注意:這里并沒有執行函數func = item[1]#這里必須要跳出循環break#判斷func變量不為Noneif func:ret = func(environ) # 執行視圖函數,必須傳入environreturn [ret] # 返回給瀏覽器else:return [b"<h1>404</h1>"] # 輸出404頁面#不寫ip,默認監聽本機ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監聽HTTP請求: httpd.serve_forever() View Code重啟py文件,再次訪問上面3個url。訪問結果一致,說明ok了。
?
拆分代碼
上面的代碼,邏輯部分都集中在一個py中,這樣不方便以后的擴展。
比如有30個url,寫30個if判斷嗎?太low了。需要解耦
1. 分離url
新建urls.py文件,代碼如下:
import viewsurlpatterns=[("/login/",views.login),("/index/",views.index),] View Code2.分離視圖函數
新建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文件
創建目錄templates,將index.html和login.html移動到此目錄
4.修改wsgiref_start.py文件,代碼如下:
from wsgiref.simple_server import make_server from urls import urlpatterns # 導入自定義的urls模塊#所有請求信息都在environ,它會傳給application def application(environ, start_response):#當前訪問路徑current_path = environ.get("PATH_INFO")print(current_path)#響應給客戶端200狀態start_response('200 OK', [('Content-Type', 'text/html')])#初始變量func = None#遍歷url列表for item in urlpatterns:#當列表的url和當前訪問路徑相同時if item[0] == current_path:#將視圖函數賦值給func,注意:這里并沒有執行函數func = item[1]#這里必須要跳出循環break#判斷func變量不為Noneif func:ret = func(environ) # 執行視圖函數,必須傳入environreturn [ret] # 返回給瀏覽器else:return [b"<h1>404</h1>"] # 輸出404頁面#不寫ip,默認監聽本機ip地址 httpd = make_server('', 8888, application)print('Serving HTTP on port 8888...') # 開始監聽HTTP請求: httpd.serve_forever() View Code重啟pwsgiref_start.py文件,再次訪問上面3個url。訪問結果一致,說明ok了。
到這里,一個簡單web框架,就完成了!
將當前文件夾打包,扔到別的電腦或者服務器,只有有python環境,就可以運行了。
?
新整頁面
修改urls.py,增加一個路徑timer,用來顯示當前時間
import viewsurlpatterns=[("/login/",views.login),("/index/",views.index),("/timer/",views.timer) ] View Code修改views.py,增加一個視圖函數
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): # 返回當前時間# 獲得當前時間now = datetime.datetime.now()# 轉換為指定的格式:otherStyleTime = now.strftime("%Y-%m-%d %H:%M:%S")return otherStyleTime.encode('utf-8') # 必須為bytes類型 View Code重啟pwsgiref_start.py文件,訪問url:
http://127.0.0.1:8888/timer/
頁面輸出:
?
參考資料:
Django 基礎教程
轉載聲明:
作者:肖祥
出處:?https://www.cnblogs.com/xiao987334176/
轉載于:https://www.cnblogs.com/bqwzx/p/10177401.html
總結
以上是生活随笔為你收集整理的python 全栈开发,Day66(web应用,http协议简介,web框架)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 校内oj??!
- 下一篇: python基础-分支判断语句(4)