web應用程序是一種能夠經過Web訪問的應用程序,程序的最大好處是用戶很容易訪問應用程序,用戶只須要有瀏覽器便可,不須要再安裝其餘軟件。應用程序有兩種模式C/S、B/S。C/S是客戶端/服務器端程序,也就是說這類程序通常獨立運行。而B/S就是瀏覽器端/服務器端應用程序,這類應用程序通常藉助谷歌,火狐等瀏覽器來運行。WEB應用程序通常是B/S模式。Web應用程序首先是「應用程序」,和用標準的程序語言,如java,python等編寫出來的程序沒有什麼本質上的不一樣。在網絡編程的意義下,瀏覽器是一個socket客戶端,服務器是一個socket服務端。html
B/S架構,是瀏覽器先發送請求,服務器響應請求,返回數據給客戶端。java
舉例:node
新建server.py文件,代碼以下:python
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()
啓動py文件,頁面訪問url:mysql
http://127.0.0.1:8800/web
網頁輸出:面試
上面提示無效的響應,爲何?是由於服務器響應信息,不符合HTTP規範。sql
查看pycharm控制檯的輸出信息npm
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'
它纔是一個完整的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()
重啓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>
修改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()
重啓py文件,再次訪問頁面,效果以下:
HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於萬維網(WWW:World Wide Web )服務器與本地瀏覽器之間傳輸超文本的傳送協議。
HTTP是一個屬於應用層的面向對象的協議,因爲其簡捷、快速的方式,適用於分佈式超媒體信息系統。它於1990年提出,通過幾年的使用與發展,獲得不斷地完善和擴展。HTTP協議工做於客戶端-服務端架構爲上。瀏覽器做爲HTTP客戶端經過URL向HTTP服務端即WEB服務器發送全部請求。Web服務器根據接收到的請求後,向客戶端發送響應信息。
http協議是基於TCP/IP協議之上的應用層協議。
請求協議(瀏覽器-->服務器)
響應協議(服務器-->瀏覽器)
好比:張三要發送一段信息爲李四。發送的信息爲
s = "zhangsan--24--shanghai"
那麼李四接收的時候,必須用--切割才能獲得信息。不然李四不知道,這段信息是幹啥的。這個是一個簡單的基於內容的協議。
對於HTTP而言,服務器和瀏覽器雙方遵循了共同的協議。
沒有請求,就沒有響應
HTTP協議規定,請求從客戶端發出,最後服務器端響應該請求並 返回。換句話說,確定是先從客戶端開始創建通訊的,服務器端在沒有 接收到請求以前不會發送響應
HTTP是一種不保存狀態,即無狀態(stateless)協議。HTTP協議 自身不對請求和響應之間的通訊狀態進行保存。也就是說在HTTP這個 級別,協議對於發送過的請求或響應都不作持久化處理。
使用HTTP協議,每當有新的請求發送時,就會有對應的新響應產 生。協議自己並不保留以前一切的請求或響應報文的信息。這是爲了更快地處理大量事務,確保協議的可伸縮性,而特地把HTTP協議設計成 如此簡單的。但是,隨着Web的不斷髮展,因無狀態而致使業務處理變得棘手 的狀況增多了。好比,用戶登陸到一家購物網站,即便他跳轉到該站的 其餘頁面後,也須要能繼續保持登陸狀態。針對這個實例,網站爲了能 夠掌握是誰送出的請求,須要保存用戶的狀態。HTTP/1.1雖然是無狀態協議,但爲了實現指望的保持狀態功能, 因而引入了Cookie技術。有了Cookie再用HTTP協議通訊,就能夠管 理狀態了。有關Cookie的詳細內容稍後講解。
無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。
好比訪問jd網頁,服務器響應請求,返回html代碼給瀏覽器。瀏覽器接收後,鏈接就斷開了。
擴展:還有一個短鏈接,好比請求響應以後,維持3秒。若是客戶端沒有操做,鏈接就斷開了。
http協議包含由瀏覽器發送數據到服務器須要遵循的請求協議與服務器發送數據到瀏覽器須要遵循的請求協議。用於HTTP協議交互的信被爲HTTP報文。請求端(客戶端)的HTTP報文 作請求報文,響應端(服務器端)的 作響應報文。HTTP報文自己是由多行數據構成的字 文本。
注意:name=ueno&age=37 上面有一個空行。
Host和Conten-Length 之間的內容屬於請求體,它是用來解釋本次請求的信息。
請求協議(瀏覽器-->服務器)
" 請求首行:請求協議 url 請求方式 請求頭: 它是key:value形式的數據 請求體...(注意:請求體和請求頭,必須有一個空行,也就是/r/n) "
看下面2個url
https://passport.jd.com/new/login.aspx?
ReturnUrl=http%3A%2F%2Fhome.jd.com%2F http://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'
第一次,是正常請求。第二次是,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
能夠看到,GET的數據,是放到url後面的。POST數據是放在請求體後面的。
打個比方:好比早期時候,用的信封。get至關於,直接寫在封面上了。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>
重啓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'
看最後的信息,就能夠看到user=xiao&pwd=123'
修改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>
輸入用戶名和密碼,點擊提交
發現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()
重啓socket.py,打開控制檯,查看網絡。訪問網頁http://127.0.0.1:8800/
發現多了一個響應頭
請求頭和響應頭,都是頗有意義的
請求頭有啥用呢?
好比這個:
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
爬蟲應用,若是沒有帶user-agent。那麼服務器,就拒絕請求。
響應式頁面,也是經過user-agent來判斷的
狀態碼的職 是當客戶端向服務器端發送請求時, 返回的請求 結果。藉助狀態碼,用戶能夠知道服務器端是正常 理了請求,仍是出 現了 。狀態碼如200 OK,以3位數字和緣由 成。數字中的 一位指定了響應 別,後兩位無分 。響應 別有以5種。
之後會大量用到3xx狀態
永久性重定向。該狀態碼錶示請求的資源已被分配了新的UR1,之後應使用資源如今所指的URI。也就是說,若是已經把資源對應的UR1保存爲書籤了,這時應該按Location首部字段提示的UR1從新保存。
像下方給出的請求URI,當指定資源路徑的最後忘記添加斜槓"/",就會產生301狀態碼。
http://example.com/sample
301使用2次請求。 一次是初始請求,第二次是訪問新的連接。
臨時性重定向。該狀態碼錶示請求的資源已被分配了新的URI,但願用戶(本次)能使用新的URI訪問。
和301MovedPermanently狀態碼類似,但302狀態碼錶明的資源不是被永久移動,只是臨時性質的。換句話說,已移動的資源對應的URI未來還有可能發生改變。好比,用戶把UR丨保存成書籤,但不會像301狀態碼出現時那樣去更新書籤,而是仍舊保留返回302狀態碼的頁面對應的UR1。
注意:面試會問道301和302的區別
《HTTP圖解》這本書能夠看一下
還有一本,《HTTP權威指南》這個太複雜了,目前能夠不看。
Web框架(Web framework)是一種開發框架,用來支持動態網站、網絡應用和網絡服務的開發。這大多數的web框架提供了一套開發和部署網站的方式,也爲web行爲提供了一套通用的方法。web框架已經實現了不少功能,開發人員使用框架提供的方法而且完成本身的業務邏輯,就能快速開發web應用了。瀏覽器和服務器的是基於HTTP協議進行通訊的。也能夠說web框架就是在以上十幾行代碼基礎張擴展出來的,有不少簡單方便使用的方法,大大提升了開發的效率。
最簡單的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
要獲得/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()
運行py文件,訪問頁面
若是訪問頁面失敗,嘗試換一個端口,就能夠了。
訪問首頁,查看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'}
PATH_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>
建立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>
編輯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()
重啓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 data def 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變量不爲None if func: ret = func(environ) # 執行視圖函數,必須傳入environ return [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()
重啓py文件,再次訪問上面3個url。訪問結果一致,說明ok了。
上面的代碼,邏輯部分都集中在一個py中,這樣不方便之後的擴展。
好比有30個url,寫30個if判斷嗎?太low了。須要解耦
1. 分離url
新建urls.py文件,代碼以下:
import views urlpatterns=[ ("/login/",views.login), ("/index/",views.index), ]
2.分離視圖函數
新建views.py文件,代碼以下:
def login(environ): with open("templates/login.html", "rb") as f: data = f.read() return data def index(environ): with open("templates/index.html" , "rb") as f: data = f.read() return data
3.分離模板文件,好比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變量不爲None if func: ret = func(environ) # 執行視圖函數,必須傳入environ return [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()
重啓pwsgiref_start.py文件,再次訪問上面3個url。訪問結果一致,說明ok了。
到這裏,一個簡單web框架,就完成了!
將當前文件夾打包,扔到別的電腦或者服務器,只有有python環境,就能夠運行了。
修改urls.py,增長一個路徑timer,用來顯示當前時間
import views urlpatterns=[ ("/login/",views.login), ("/index/",views.index), ("/timer/",views.timer) ]
修改views.py,增長一個視圖函數
import datetime def login(environ): with open("templates/login.html", "rb") as f: data = f.read() return data def index(environ): with open("templates/index.html" , "rb") as f: data = f.read() return data def timer(environ): # 返回當前時間 # 得到當前時間 now = datetime.datetime.now() # 轉換爲指定的格式: otherStyleTime = now.strftime("%Y-%m-%d %H:%M:%S") return otherStyleTime.encode('utf-8') # 必須爲bytes類型
重啓pwsgiref_start.py文件,訪問url:
http://127.0.0.1:8888/timer/
頁面輸出: