學習web框架以前,先來看一下web基礎知識,首先要明白其本質就是socket,用戶對應一個socket客戶端,可是若是從socket開始開發web應用程序那麼效率太了,正確的作法是底層socket處理代碼由專門的服務器軟件實現,而對於真實開發中的python web程序來講也是通常會分爲兩部分:服務器程序和應用程序。服務器程序負責對socket服務器進行封裝,並在請求到來時,先通過web服務器,對請求的各類數據進行整理封裝。以後web服務器將封裝好的數據傳遞給應用程序,應用程序並不涉及底層的socket,由應用程序負責具體的邏輯處理,可是上面就有一個問題,那就是如何來定義一個統一的web服務器和web應用程序之間接口格式,這個接口就是WSGI,WSGI(Web Server Gateway Interface)是一種規範,它定義了使用python編寫的web app與web server之間接口格式。WSGI applications 能夠是棧式的,這個棧的中間部分能夠叫作中間件
,兩端是必需要實現的application和server。python標準庫提供的獨立WSGI服務器稱爲wsgiref。下面咱們來看一下如何簡單的自定義一個web框架。javascript
HTTP請求的全部輸入信息均可以經過environ
得到,HTTP響應的輸出均可以經過start_response()
加上函數返回值做爲Body。下面來看一下用最簡單的wsgiref來實現一個web框架。css
from wsgiref.simple_server import make_server def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) body = '<h1>hello {}</h1>'.format(environ['PATH_INFO'][1:] or 'web') return [body.encode('utf-8')] # return [b'<h1>Hello, web!</h1>'] # 這裏須要注意格式 httpd = make_server('', 8000, application) httpd.serve_forever()
上面的東西寫得太簡單了,一個簡單的框架還應該包括路由系統和模板引擎等基本的東西,下面咱們來看一下如何本身來簡單的實現一下增強版的web框架html
from wsgiref.simple_server import make_server import time def new(): f = open('s1.html', 'r') data = f.read() f.close() # 模擬模板引擎處理數據,將html裏文件的item替換成其餘數據 # 這裏僅僅是用字符串的替換來最簡單的說明下原理 new_data = data.replace("{{item}}", str(time.time())) return new_data def index(): f = open('index.html', 'r') //打開文件讀取內容,將內容看成字符串返回給用戶 data = f.read() f.close() return data def home(): return 'home' URLS = { # 定義一個字典,簡單模擬路由系統 "/new": new, # 一個url對應一個函數處理 "/index": index, "/home": home, } def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] if url in URLS.keys(): # 不一樣的url執行不一樣的函數 func_name = URLS[url] ret = func_name() else: ret = "404" return [ret.encode('utf-8')] # 最後將結果返回 httpd = make_server('', 8000, application) httpd.serve_forever()
雖然上面的代碼能夠用簡單的字符串進行替換,來動態的返回內容,可是效率極其的低下,最好的作法是使用jinja2模板引擎。至於如何使用,這裏先不講,先講下tronadao,而後咱們看一個真正的web框架是如何使用路由系統,和模板引擎。java
雖然咱們本身寫框架,可是功能效率不行,這裏看一下比較常見的web框架tronado,Tornado爲python的一個非阻塞異步web frame,不一樣於那些最多隻能達到10,000個併發鏈接的傳統網絡服務器,Tornado在設計之初就考慮到了性能因素,旨在解決C10K問題,這樣的設計使得其成爲一個擁有很是高性能的框架。此外,它還擁有處理安全性、用戶驗證、社交網絡以及與外部服務(如數據庫和網站API)進行異步交互的工具。python
下面來看一下最簡單的hello,world, jquery
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
上述代碼執行的具體過程以下: web
第一步:執行腳本,監聽 8888 端口數據庫
第二步:瀏覽器客戶端訪問 /index --> http://127.0.0.1:8888/indexnpm
第三步:服務器接受請求,並交由對應的類處理該請求瀏覽器
第四步:類接受到請求以後,根據請求方式(post / get / delete ...)的不一樣調用並執行相應的方法
第五步:方法返回值的字符串內容發送瀏覽器
web框架其中一個關鍵的地方就是路由系統,路由系統說簡單的就是,一個url對應一個類(其餘框架裏面可能對應的是函數),而後相應的類裏面定義get,post等方法來處理不一樣的請求。並且tronado支持2級路由。下面來看一下代碼實現
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") class StoryHandler(tornado.web.RequestHandler): def get(self, story_id): self.write("You requested the story " + story_id) class BuyHandler(tornado.web.RequestHandler): def get(self): self.write("it.wxtrkbc.com/index") application = tornado.web.Application([ (r"/index", MainHandler), (r"/story/([0-9]+)", StoryHandler), # 後面的分頁功能就是基於此實現的 ]) application.add_handlers('it.wxtrkbc.com$', [ # 這裏添加2級域名,實驗的話須要改本地host (r'/index', BuyHandler), # it.wxtrkbc.com:8888/index ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
模板引擎說簡單點就是將本來的html的某些內容用一些特殊的字符串代替,而後在處理用戶的不一樣的請求時,將html的字符串替換掉,返回給用戶新的一個字符串,這樣就達到了動態的html的效果。Tornado的模板支持「控制語句」和「表達語句」,控制語句是使用 {%
和 %}
包起來的 例如 {% if len(items) > 2 %}
。表達語句是使用 {{
和 }}
包起來的,例如 {{ items[0] }}
。控制語句和對應的 Python 語句的格式基本徹底相同。咱們支持 if
、for
、while
和 try
,這些語句邏輯結束的位置須要用 {% end %}
作標記。還經過 extends
和 block
語句實現了模板繼承,此外還能夠自定義UIMethod以UIModule。下面咱們來簡單的看一下用法
首先看一下html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="static/s1.css" rel="stylesheet"/> <script src="static/s1.js"></script> </head> <body> <div> <h2>提交內容</h2> <form method="post" action="/index"> <input type="text" name="xxx"> <input type="submit" value="提交"> </form> <h2>顯示內容</h2> <ul> {% for item in inp %} //for循環 <li>{{item}}</li> {% end %} </ul> <h3>{{npm}}</h3> <h3>{{func(npm)}}</h3> // 自定義UIMethod <h3>{% module custom() %}</h3> // 自定義UIModule </div> </body> </html>
下面來看一下py文件
import tornado.web import tornado_s1.uimodule as md from tornado_s1 import uimethod as mt INPUT_LIST = [11, 22 class MainHandler(tornado.web.RequestHandler): def get(self): self.render("s1.html", npm='NPM', inp=INPUT_LIST) def post(self): username = self.get_argument('xxx', None) INPUT_LIST.append(username) self.render("s1.html", npm='NPM', inp=INPUT_LIST) settings = { 'template_path': 'template', 'static_path': 'static', # 'static_url_prefix': '/ss/', 'ui_methods': mt, 'ui_modules': md, } application = tornado.web.Application([(r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
最後來看一下自定義的module和method,這裏只是簡單的演示,因此代碼比較簡單
uimethod.py def func(self, arg): return arg.lower() uimodule.py from tornado.web import UIModule from tornado import escape class custom(UIModule): def render(self, *args, **kwargs): return '123'
將一些公用的html,css等寫到通用的文件,而後經過繼承,就能夠獲取母板的內容,而繼承的html裏面只須要寫特有的東西,模板繼承的功能很是實用,而靜態緩存則能夠減小相應的請求資源,下面來看一下具體怎麼用這裏我就只簡單看一下html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="{{static_url('css/chouti.css')}}" type="text/css" rel="stylesheet"> // 經過static_url引用靜態文件 {% block css %} {% end %} </head> <body> <div class="header"> <div class="header-content"> {% if user_info['is_login'] %} <div class="account"> <a href="#">{{user_info['username']}}</a> <a href="/logout">退出</a> </div> {% else %} <div class="account"> <a href="http://127.0.0.1:8888/register">註冊</a> <a href="http://127.0.0.1:8888/login">登錄</a> </div> {% end %} </div> </div> <div class="content"> {% block body %} {% end %} </div> <a class="back-to-head" href="javascript:scroll(0,0)"></a> {% block js %} {% end %} <script> </script> </body> </html>
下面來看一會兒模板的html文件
{% extends '../base/layout.html' %} {% block css %} <link href="{{static_url('css/css/common.css')}}" rel="stylesheet"> <link href="{{static_url('css/css/login.css')}}" rel="stylesheet"> {% end %} {% block body %} {% end %} {% block js %} <script src="{{static_url('js/jquery-1.12.4.js')}}"></script> <script src="{{static_url('js/login.js')}}"></script> {% end %}
下面來簡單的說如下tronado模板引擎的編譯原理,具體的實現原理等後面有時間再來補充,