python筆記1:自制WSGI的web服務器、框架

Table of Contents

 

06WSGI

07服務器支持WSGI

08服務器傳遞需要的字典參數

09 框架獲取頁面模板數據

10 添加配置文件、shell功能


簡單服務器背景知識

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)

解耦不夠徹底:

  1. 所有靜態文件都在static文件夾打開
  2. 模塊要放在./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等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章