Table of Contents
簡單服務器背景知識
02面向對象服務器
需要http協議的web服務器
03動態解析
迴應的時候不止直接返回header+文件
Body中返回變量的字符串-->解耦,調用web框架(負責邏輯)
04
動態的網站是實時生成的
05 將web服務器和邏輯處理分開
Web框架寫完login()方法
在服務器import web_frame
Body= web_frame.login()—這一部分依然沒有解耦
需要只調用一個函數
在web_frame中定義application函數
服務器將文件名傳入application,用body接收返回值
Application根據文件名自己調用
06WSGI
動態請求對應框架中的函數
使用著名服務器Nginx等時,不能在已有服務器中導入自己的模塊,如何使自己寫的框架被服務器支持?
要根據協議(WSGI)來寫框架(Flask、Django等)
WSGI規定瀏覽器請求內容,應返回數據:header+body
WSGI接口定義,要求開發者實現application函數,響應HTTP請求:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return '<h1>Hello, web!</h1>' |
參數:environ字典,start_response函數的引用(函數名)
start_response是服務器定義的函數,可以通過這個引用調用此函數
header通過調用start_response參數傳遞給服務器
Body直接用return返回
07服務器支持WSGI
調用函數運走了一份header,我再return他一個body
服務器把header和body加起來
調用start_response時傳入參數:1.狀態碼 2.元組列表,其中每個元組包括response需要的k-v(對應一行信息)
服務器取出框架通過start_response傳來的headers實例屬性:
for temp in self.headers: header+=”%s:%s\r\n” % (temp[0], temp[1]) |
08服務器傳遞需要的字典參數
框架需要告訴服務器用了什麼編碼語音(放在'Content-Type後面)
服務器版本:Server不應該由框架決定、傳遞,在服務器中加
env=dict()目前還是空字典
需要訪問不同的py,返回不同頁面,則使用它,接收的filename是字符串
服務器 env[‘PATH_INFO’]=file_name |
框架 filename=env[‘PATH_INFO’] |
09 框架獲取頁面模板數據
將frame文件單獨放一個文件夾dynamic,新建__init__.py作爲包,可被import
import dynamic.mini_frame #調用 dynamic.mini_frame |
原本框架接收application參數後只是調用對應函數返回值,現需改成頁面數據:
可直接return含大段html的字符串,這時link引用的css和js還無法獲取
服務器接收到. css等靜態文件需要去static文件夾找
f=open(“./static”+filename,”rb”) |
框架返回時讀文件返回,而不要直接返回html字符串:
with open(“./templates/index.html”) as f: content=f.read() return content |
如果打開../templates/index.html(相對路徑上一級)則無法找到,因爲運行的是服務器文件,所有路徑都從運行py開始算,不管打開文件的代碼是不是在其他包內
一般情況下,服務器和框架是分開的,框架作爲可導入的包,服務器加載框架中的.py,框架讀取templates中的html文件,並作爲字符串返回給服務器
10 添加配置文件、shell功能
需求:能否改端口、不修改服務器支撐其他框架
給程序傳參:
python3 web_server.py 7788 mini_frame:application |
import sys print(sys.argv) |
接收到參數[‘’test.py,‘7788’,’ mini_frame’]是字符串,不是數字 |
在服務器中:
import sys def main(): if len(sys.argv)==3: try: port=int(sys.argv[1]) except Exception as ret: print(“端口應該爲數字”) return else: print(“運行格式:python3 xxxx.py 7890 mini_frame:application”) return wsgi_server=WSGIServer(port) wsgi_server.run_forever() |
class WSGIServer(object): def __init__(self,port): self.tcp_server_socket.bind(“”,port) |
指定模塊運行,原先導入了dynamic.mini_frame,但應該是通用的,故作爲參數
python3 web_server.py 7788 mini_frame:application |
如何找到該函數:在獲取該參數後import,它把直接寫的作爲模塊名而非變量解析,但__import__()可以放變量,返回的對象標記着導入的這個模塊, 則getattr返回值指向該模塊application
sys.path.append()可添加系統環境路徑
frame_app_name=sys.argv[2] re.match(r”({[^:]+}):({.*})”, frame_app_name) #前半部分不是冒號的至少有一個 if ret: frame_name=ret.ret.group(1) app_name=ret.ret.group(1) else: print(“運行格式:python3 xxxx.py 7890 mini_frame:application”) sys.path.append(“./dynamic”) frame=__import__(frame_name) app=getattr(frame,appn_name)
wsgi_server=WSGIServer(port,app) |
使用時
self.application=app body=self.application(env,self.set_response_header) |
解耦不夠徹底:
- 所有靜態文件都在static文件夾打開
- 模塊要放在./dynamic
支持配置文件:
創建web_server.conf
存儲靜態文件和框架加載路徑
{ "static_path":"./static", "synamic_path":"./dynamic" } |
在服務器中讀取文件字符串,轉成字典
with open("./web_server.conf") as f: conf_info=eval(f.read())#eval轉成字典 sys.path.append(conf_info['dynamic_path'])
wsgi_server=WSGIServer(port,app,conf_info['static_path']) |
#使用
self.static_path=static_path#init中 f=open(self.static_path+filename,"rb") |
shell腳本(放Linux命令):vim run.sh
python3 web_server.py 7890 miniframe:application |
添加可執行權限x:
chmod +x run.sh ./run.sh |
添加readme.txt:
運行方式./run.sh或python3 web_server.py 7890 miniframe:application
version等