標籤:set 初始化 mat open insert 執行 end 使用者 str
為什麼會有web架構
有了上一篇內容,靜態、動態web伺服器的實現,已經掌握了用戶端請求到伺服器處理的機制。在動態資源處理中,根據請求 .py 匯入模組應用,然後調用應用入口程式實現動態處理。但是在真實的項目中,肯定有很多應用(.py檔案),按照這種處理機制(每個應用中都要寫一個入口,設定狀態代碼、headers)不符合架構設計原則,這一部分肯定是可以複用的,所用應用應該公用一個入口,這就引入一個概念web架構。
設計思路
- 把每個應用中入口 application(env,start_response) 提取到web伺服器中,然後在入口中通過路由機制分發到各應用中處理。
代碼如下:- WebFramework.py
`# coding:utf-8import timefrom MyWebServer import WebServerHTML_ROOT_DIR = "./static"class Application(object): ‘‘‘自訂通用的web架構‘‘‘ # 初始化路由資訊 def __init__(self,urls): self.urls = urls # 匹配路由 def __call__(self, env, start_response): path = env.get("PATH_INFO", "/") # /static/index.html if path.startswith("/static"): file_name = path[7:] # 開啟檔案,讀取內容 try: file = open(HTML_ROOT_DIR + file_name, "rb") except IOError: # 代表未找到路由資訊,404錯誤 status = "404 Not Found" headers = [] start_response(status, headers) return "not found" else: file_data = file.read() file.close() status = "200 OK" headers = [] start_response(status, headers) return file_data.decode("utf-8") for url,handler in self.urls: if path == url: return handler(env,start_response) # 未匹配到 status = ‘404 Not Found‘ headers = [] start_response(status,headers) return ‘not found‘def showtime(env,start_response): status = ‘200 OK‘ headers = [ (‘Content-Type‘, ‘text/plain‘) ] start_response(status, headers) return str(time.time())def sayhello(env,start_response): status = ‘200 OK‘ headers = [ (‘Content-Type‘,‘text/plain‘) ] start_response(status,headers) return ‘say hello‘def helloworld(env,start_response): status = ‘200 OK‘ headers =[ (‘Content-Type‘,‘text/plain‘) ] start_response(status,headers) return ‘hello world‘if __name__ == ‘__main__‘: urls = [ (‘/‘, showtime), (‘/sayhello‘,sayhello), (‘/helloworld‘,helloworld) ] app = Application(urls) webServer = WebServer(app) webServer.bind(8000) webServer.start()`
- MyWebServer.py
`# coding:utf-8import socketimport reimport sysfrom multiprocessing import ProcessHTML_ROOT_DIR = ‘./static‘WSGI_PY = ‘./wsgipy‘class WebServer(object): ‘‘‘ 簡單的webserver ‘‘‘ def __init__(self,application): ‘‘‘application:架構‘‘‘ self.sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.app = application def start(self): self.sock_server.listen(128) while True: sock_client, addr = self.sock_server.accept() print(‘[%s,%s]使用者已串連......‘ % addr) handle_client_process = Process(target=self.handle_client, args=(sock_client,)) handle_client_process.start() sock_client.close() def start_response(self, status, headers): """ status = "200 OK" headers = [ ("Content-Type", "text/plain") ] star """ resp_headers = ‘HTTP/1.1 ‘ + status + ‘\r\n‘ for header in headers: resp_headers += ‘%s: %s\r\n‘ % header self.resp_headers = resp_headers def handle_client(self, sock_client): ‘‘‘處理用戶端請求‘‘‘ recv_data = sock_client.recv(1024) #print(‘請求資料:‘, recv_data) req_lines = recv_data.splitlines() #for line in req_lines: # print(line) req_start_line = req_lines[0] #print(req_start_line.decode(‘utf-8‘)) file_name = re.match(r"\w+ +(/[^ ]*) ", req_start_line.decode("utf-8")).group(1) method = re.match(r"(\w+) +/[^ ]* ", req_start_line.decode("utf-8")).group(1) env = { "PATH_INFO": file_name, "METHOD": method } response_body = self.app(env, self.start_response) response = self.resp_headers + "\r\n" + response_body # 向用戶端返迴響應資料 sock_client.send(bytes(response, "utf-8")) # 關閉用戶端串連 sock_client.close() def bind(self, port): self.sock_server.bind((‘‘, port))def main(): sys.path.insert(1,WSGI_PY) webServer = WebServer() webServer.bind(8000) webServer.start()if __name__ == ‘__main__‘: main()`
啟動
入口實在WebFramework.py 中,直接啟動該檔案,用戶端請求指定路徑即可看到效果。但是對於大多架構都是通過命令列啟動,我們不可能在程式中這樣啟動,而且對於webserver來說始終是不變得,變的是web架構,所以改造成命令列啟動,代碼如下:
- WebFramework.py
`# coding:utf-8import timeHTML_ROOT_DIR = "./static"class Application(object): ‘‘‘自訂通用的web架構‘‘‘ # 初始化路由資訊 def __init__(self,urls): self.urls = urls # 匹配路由 def __call__(self, env, start_response): path = env.get("PATH_INFO", "/") # /static/index.html if path.startswith("/static"): file_name = path[7:] # 開啟檔案,讀取內容 try: file = open(HTML_ROOT_DIR + file_name, "rb") except IOError: # 代表未找到路由資訊,404錯誤 status = "404 Not Found" headers = [] start_response(status, headers) return "not found" else: file_data = file.read() file.close() status = "200 OK" headers = [] start_response(status, headers) return file_data.decode("utf-8") for url,handler in self.urls: if path == url: return handler(env,start_response) # 未匹配到 status = ‘404 Not Found‘ headers = [] start_response(status,headers) return ‘not found‘def showtime(env,start_response): status = ‘200 OK‘ headers = [ (‘Content-Type‘, ‘text/plain‘) ] start_response(status, headers) return str(time.time())def sayhello(env,start_response): status = ‘200 OK‘ headers = [ (‘Content-Type‘,‘text/plain‘) ] start_response(status,headers) return ‘say hello‘def helloworld(env,start_response): status = ‘200 OK‘ headers =[ (‘Content-Type‘,‘text/plain‘) ] start_response(status,headers) return ‘hello world‘urls = [ (‘/‘, showtime), (‘/sayhello‘,sayhello), (‘/helloworld‘,helloworld)]app = Application(urls)# if __name__ == ‘__main__‘:# urls = [# (‘/‘, showtime),# (‘/sayhello‘,sayhello),# (‘/helloworld‘,helloworld)# ]# app = Application(urls)## webServer = WebServer(app)# webServer.bind(8000)# webServer.start() `
- MyWebServer.py
`# coding:utf-8import socketimport reimport sysfrom multiprocessing import ProcessHTML_ROOT_DIR = ‘./static‘WSGI_PY = ‘./wsgipy‘class WebServer(object): ‘‘‘ 簡單的webserver ‘‘‘ def __init__(self,application): ‘‘‘application:架構‘‘‘ self.sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.app = application def start(self): self.sock_server.listen(128) while True: sock_client, addr = self.sock_server.accept() print(‘[%s,%s]使用者已串連......‘ % addr) handle_client_process = Process(target=self.handle_client, args=(sock_client,)) handle_client_process.start() sock_client.close() def start_response(self, status, headers): """ status = "200 OK" headers = [ ("Content-Type", "text/plain") ] star """ resp_headers = ‘HTTP/1.1 ‘ + status + ‘\r\n‘ for header in headers: resp_headers += ‘%s: %s\r\n‘ % header self.resp_headers = resp_headers def handle_client(self, sock_client): ‘‘‘處理用戶端請求‘‘‘ recv_data = sock_client.recv(1024) #print(‘請求資料:‘, recv_data) req_lines = recv_data.splitlines() #for line in req_lines: # print(line) req_start_line = req_lines[0] #print(req_start_line.decode(‘utf-8‘)) file_name = re.match(r"\w+ +(/[^ ]*) ", req_start_line.decode("utf-8")).group(1) method = re.match(r"(\w+) +/[^ ]* ", req_start_line.decode("utf-8")).group(1) env = { "PATH_INFO": file_name, "METHOD": method } response_body = self.app(env, self.start_response) response = self.resp_headers + "\r\n" + response_body # 向用戶端返迴響應資料 sock_client.send(bytes(response, "utf-8")) # 關閉用戶端串連 sock_client.close() def bind(self, port): self.sock_server.bind((‘‘, port))def main(): sys.path.insert(1,WSGI_PY) if len(sys.argv) < 2: sys.exit("python MyWebServer.py Module:app") module_name, app_name = sys.argv[1].split(‘:‘) m = __import__(module_name) app = getattr(m,app_name) webServer = WebServer(app) webServer.bind(8000) webServer.start()if __name__ == ‘__main__‘: main()`
調用方式
在命令列中,執行如下命令即可啟動webserver服務,模組名稱:應用程式框架名, 其中這裡的app指的是WebFramework.py中Application類的執行個體,其實也就是應用程式框架的執行個體。後續自訂架構的時候,直接調用即可。
`python MyWebServer.py WebFramework:app `
代碼已上傳到github:python 小程式
Web架構的引入