




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
零基礎(chǔ)小白必看篇:從0到1構(gòu)建PythonWeb框架造輪子是最好的一種學(xué)習(xí)方式,本文嘗試從0開始造個PythonWeb框架的輪子,我稱它為ToyWebF。本文操作環(huán)境為:MacOS,文中涉及的命令,請根據(jù)自己的系統(tǒng)進(jìn)行替換。ToyWebF的簡單特性:支持多種不同形式的路由注冊方式支持靜態(tài)HTML、CSS、JavaScript支持自定義錯誤支持中間件下面我們來實(shí)現(xiàn)這些特性。最簡單的web服務(wù)首先,我們需要安裝gunicorn,回憶一下Flask框架,該框架有內(nèi)置的Web服務(wù)器,但不穩(wěn)定,所以上線時通常會替換成uWSGI或gunicorn,這里不搞這個內(nèi)置Web服務(wù),直接使用gunicorn。這里多說一句,小編是一名python開發(fā)工程師,這里有我自己整理的一套最新的python系統(tǒng)學(xué)習(xí)教程,包括從基礎(chǔ)的python腳本到web開發(fā)、爬蟲、數(shù)據(jù)分析、數(shù)據(jù)可視化、機(jī)器學(xué)習(xí)等。想要這些資料的可以關(guān)注小編,并在后臺私信小編即可領(lǐng)取。我們創(chuàng)建新的目錄與Python虛擬環(huán)境,在該虛擬環(huán)境中安裝gunicornmkdirToyWebFpython3-mvenvvenv#創(chuàng)建虛擬環(huán)境sourcevenv/bin/activat激活虛擬環(huán)境pipinstallgunicorn復(fù)制代碼在啥都沒有的情況下,構(gòu)建最簡單的Web服務(wù),在ToyWebF目錄下,創(chuàng)建app.py與api.py文件,寫入下面代碼。api.py文件classAPI:defcall(self,environ,start_response):response_body=b"Hello,World!"status="200OK"start_response(status,headers二口)returniter([response_body])app.py文件fromapiimportAPIapp=API()復(fù)制代碼運(yùn)行g(shù)unicornapp:app訪問http://127.O.O.l:80O可以看見Hello,World!但現(xiàn)在請求體中的參數(shù)在environ變量中,難以解析,我們返回的response也是bytes形式。我們可以使用webob庫,將environ中的數(shù)據(jù)轉(zhuǎn)為Request對象,將需要返回的數(shù)據(jù)轉(zhuǎn)為Response對象,處理起來更加直觀方便,直接通過pip安裝一下。pipinstallwebob復(fù)制代碼然后修改一下API類的—_call_R法,代碼如下。fromwebobimportRequest.ResponseclassAPI(object):defwsgi_app(self,environ,start_response):""通過webob將請求的環(huán)境信息轉(zhuǎn)為request對象""“request二Request(environ)response=self.handle_request(request)returnresponse(environ,start_response)defcall(self,environ,start_response):self.wsgi_app(environ,start_response)復(fù)制代碼上述代碼中,通過webob庫的Request類將environ對象(請求的環(huán)境信息)轉(zhuǎn)為容易處理的request,隨后調(diào)用handle_request方法對request進(jìn)行處理,處理的結(jié)果,通過response對象返回。handle_request方法在ToyWebF中非常重要,它會匹配出某個路由對應(yīng)的處理方法,然后調(diào)用該方法處理請求并將處理的結(jié)果返回,在解析handle_request前,需要先討論路由注冊實(shí)現(xiàn),代碼如下。classAPI(object):defini(self):#ur路由self.routes={}defroute(self,path):#添加路由的裝飾器defwrapper(handler):self.add_route(path,handler)returnhandlerreturnwrapperdefadd_route(self,path,handler):#相同路徑不可重復(fù)添加assertpathnotinself.routes,"Suchroutealreadyexists"self.routes[path]=handler復(fù)制代碼其實(shí)就是將路由和方法存到self.route字典中,可以通過route裝飾器的形式將路由和方法關(guān)聯(lián),也可以通過add_route方法關(guān)聯(lián),在app.py中使用一下。app=API()通過裝飾器關(guān)聯(lián)路由和方法@app.route("/home")defhome(request,response):response.text二"ThisisHome"路由中可以有變量,對應(yīng)的方法也需要有對應(yīng)的參數(shù)@app.route("/hello/{name}")defhello(requst,response,name):response.text二f"Hello,{name}"可以裝飾類@app.route("/book")classBooksResource(object):defget(self,req,resp):resp.text二"BooksPage"defhandlerl(req,resp):resp.text二"handlerl"可以直接通過add_route方法添加app.add_route("/handler1",handler1)復(fù)制代碼因?yàn)閡r呻可以存在變量,如@app.route("/hello/{name}")所以在匹配時,需要進(jìn)行解析,可以使用正則匹配的方式進(jìn)行匹配,parse這個第三方庫已經(jīng)幫我們實(shí)現(xiàn)了相應(yīng)的正則匹配邏輯,pip安裝使用一下則可。pipinstallparseIn[1]:fromparseimportparse匹配In[2]:res=parse("/hello/{name}",二二兩Olo/In[3]:dOut[3]:{'name'二兩'}復(fù)制代碼這里定義find_handle訪法來實(shí)現(xiàn)對self.route的遍歷。classAPI(object):deffind_handler(self,request_path):#遍歷路由forpath,handlerinself.routes.items():#正則匹配路由parse_result=parse(path,request_path)ifparse_resultisnotNone:parse_result#返回路由對應(yīng)的方法和路由本身returnhandler,parse_dreturnNone,None復(fù)制代碼了解了路由與方法關(guān)聯(lián)的原理后,就可以實(shí)現(xiàn)handle_request方法,該方法主要的路徑就是根據(jù)路由調(diào)度對應(yīng)的方法,代碼如下。importinspectclassAPI(object):defhandle_request(self,request):〃〃請求調(diào)度〃〃〃response=Response()handler,kwargs=self.find_handler(request.path)try:ifhandlerisnotNone:ifinspect.isclass(handler如果是類,則獲取其中的方法handler=getattr(handler(),request.method.lower().None)ifhandlerisNone:糞中該方法不存在,則該類不支持該請求類型raiseAttributeError(〃Methodnowallowed”,request.method)handler(request,response,**kwargs)else:#返回默認(rèn)錯誤self.defalut_response(response)exceptExceptionase:raiseereturnresponse復(fù)制代碼在該方法中,首先實(shí)例化webob庫的Response對象,然后通過self.find_handl方法獲取此次請求路由對應(yīng)的方法和對應(yīng)的參數(shù),比如。@app.route("/hello/{name}")defhello(requst,response,name):response.text二f"Hello,{name}"復(fù)制代碼它將返回hello方法對象和name參數(shù),如果是/hello二兩,那么name就是二兩。因?yàn)閞oute裝飾器可能裝飾器的類對象,比如。可以裝飾類@app.route("/book")classBooksResource(object):defget(self,req,resp):resp.text二"BooksPage"復(fù)制代碼此時self.find_handl方法返回的hanler就是個類,但我們希望調(diào)用的是類中的get、post、delete等方法,所以需要一個簡單的判斷邏輯,通過inspect.iscla方法判斷handler如果是類對象,那么就通過getatt方法獲取類對象實(shí)例的中對應(yīng)的請求方法。獲取請求方法,request.method.lower()可為get、post、deletehandler=getattr(handler(),request.method.lower(),None)復(fù)制代碼如果類對象中沒有該方法屬性,則拋出該請求類型不被允許的錯誤,如果不是類對象或類對象中存在該方法屬性,則直接調(diào)用則可。此外,如果方法的路由并沒有注冊到self.route中,即404的情況,定義了defalut_response方法返回其中內(nèi)容,代碼如下。classAPI(object):defdefalut_response(self,response):response.status_code=404response.text二"NotFound"復(fù)制代碼如果handle_request方法中調(diào)度的過程出現(xiàn)問題,則直接raise將錯誤拋出。至此,一個最簡單的web服務(wù)就編寫完成了。支持靜態(tài)文件回顧Flask,F(xiàn)lask可以支持HTML、CSS、JavaScript等靜態(tài)文件,利用模板語言,可以構(gòu)建出簡單但美觀的Web應(yīng)用,我們讓TopWebF也支持這一功能,最終實(shí)現(xiàn)圖中的網(wǎng)站,完美兼容靜態(tài)文件。Flask使用了jinja2乍為其html模板引擎,ToyWebF同樣使用jinja2jinja2其實(shí)實(shí)現(xiàn)一種簡單的DSL(領(lǐng)域內(nèi)語言),讓我們可以在HTML中通過特殊的語法改變HTML的結(jié)構(gòu),該項目非常值得研究學(xué)習(xí)。首先pipinstalljinj然2后就可以使用它了,在ToyWebF項目目錄中創(chuàng)建templates目錄,以該目錄作為默認(rèn)的HTML文件根目錄,代碼如下。fromjinja2importEnvironment,FileSystemLoaderclassAPI(object):definifself,templates_dir二"templates"):#html文件夾self.templates_env=Environment(loader二FileSystemLoader(os.path.abspath(self.templates_dir)))deftemplate(self,template_name,context二None):返回模板內(nèi)容ifcontextisNone:context二{}returnself.templates_env.get_template(template_name).render(**context)復(fù)制代碼首先利用jinja的FileSystemLoader類將filesyste中的某個文件夾作為loader,然后初始化Environment。在使用的過程中(即調(diào)用template方法),通過get_template方法獲得具體的某個模板并通過render方法將對應(yīng)的內(nèi)容傳遞給模板中的變量。這里我們不寫前端代碼,一直接去互聯(lián)網(wǎng)中下載模板,這里下載了Bootstrap提供的免費(fèi)模板,可以自行去/themes/freela下^er/載,下載完后,你會獲得index.html以及對應(yīng)的css、jssimg等文件,將index.html移動到ToyWebF/templates中并簡單修改了一下,添加一些變量。你好呀-{{name}}復(fù)制代碼然后在app.py文件中為index.html定義路由以及需要的參數(shù)。@app.route("/index")defindex(req,resp):template=app.template("index.html",context二{"nan二"兩"""title":"ToyWebF"})resp.body需要bytes,template方法返回的是unicodestring所以需要編碼resp.body=template.encode()復(fù)制代碼至此html文件的支持就完成了,但此時的html無法正常載入css和js導(dǎo)致頁面布局非常丑陋且交互無法使用。接著就讓ToyWebF支持css、js首先在ToyWebF目錄下創(chuàng)建stati文件夾用于存放css、js或img等靜態(tài)文件,隨后直接將前面下載的模板,其中的靜態(tài)文件復(fù)制到stati(中則可。通過whitenoise第三方庫,可以通過簡單的幾行代碼讓web框架支持css和js不需要依賴nginx等服務(wù),首先pipinstallwhitenoi隨后修改API類的__init_方法,代碼如下。classAPI(object):def__init__(self,templates_dir二"templates",static_dir="static"):html文件夾self.templates_env=Environment(loader二FileSystemLoader(os.path.abspath(self.templates_dir)))css、JavaScript文件夾self.whitenoise=WhiteNoise(self.wsgi_app,root二static_dir)復(fù)制代碼其實(shí)就是通過WhiteNoise將self.wsgi_app方法包裹起來,在調(diào)用API的__call方法時,直接調(diào)用self.whitenoiseclassAPI(object):def__call__(self,environ,start_response):returnself.whitenoise(environ,start_response)復(fù)制代碼此時,如果請求web服務(wù)獲取css、js等靜態(tài)資源,WhiteNoise會獲取其內(nèi)容并返回給client它在背后會匹配靜態(tài)資源在系統(tǒng)中對應(yīng)的文件并將其讀取返回。至此,一開始的網(wǎng)頁效果就實(shí)現(xiàn)好了。自定義錯誤web服務(wù)如果出現(xiàn)500時,默認(rèn)會返回internalservererro這顯得比較丑,為了讓框架使用者可以自定義500時返回的錯誤,需要添加一些代碼。首先API初始化時,初始self.exception_handle對象并定義對應(yīng)的方法添加自定義的錯誤classAPI(object):def__init__(self,templates_dir二"templates",static_dir="static"):#自定義錯誤self.exception_handler=Nonedefadd_exception_handler(self,exception_handler):添加自定義errorhandlerself.exception_handler=exception_handler復(fù)制代碼在handler_request方法進(jìn)行請求調(diào)度時,調(diào)度的方法執(zhí)行邏輯時報500,此時不再默認(rèn)將錯誤拋出,而是先判斷是否有自定義錯誤處理。classAPI(object):defhandle_request(self,request):〃〃請求調(diào)度〃〃〃try:..省略exceptExceptionase:為空,才返回internalservererrorifself.exception_handlerisNone:raiseeelse:#自定義錯誤返回形式self.exception_handler(request,response,e)returnresponse復(fù)制代碼在app.py中,自定義錯誤返回方法,如下。defcustom_exception_handler(request,response,exception_cls):response.text二〃0ops!Somethingwentwrong.”#自定義錯誤app.add_exception_handler(custom_exception_handler)復(fù)制代碼custom_exception_handler方法只返回自定義的一段話,你完全可以替換成美觀的template。我們可以實(shí)驗(yàn)性定義一個路由來看效果。@app.route("/error")defexception_throwing_handler(request,response):raiseAssertionError(〃Thishandlershouldnotbeuser")復(fù)制代碼支持中間件Web服務(wù)的中間件也可以理解成鉤子,即在請求前可以對請求做一些處理或者返回Response前對Response做一下處理。為了支持中間件,在TopWebF目錄下創(chuàng)建middleware.py文件,在編寫代碼前,思考一下如何實(shí)現(xiàn)?回顧一下現(xiàn)在請求的調(diào)度邏輯。1.通過routes裝飾器關(guān)聯(lián)路由和方法2.通過API.whitenoise處理3.如果是請求API接口,那么會將參數(shù)傳遞給API.wsgi_app4.API.wsgi_app最終會調(diào)用API.handle_request方法獲取路由對應(yīng)的方法并調(diào)用該方法執(zhí)行相應(yīng)的邏輯如果希望在request前以及response后做相應(yīng)的操作,那么其實(shí)就需要讓邏輯在API.handle_request前后執(zhí)行,看一下代碼。fromwebobimportRequestclassMiddleware(object):def__init__(self,app):self.app=app#AP類實(shí)例defadd(self,middleware_cls):#實(shí)例化Middleware對象,包裹self.appself.app=middleware_cls(self.app)defprocess_request(self,req):request前要做的處理passdefprocess_response(self,req,resp):response后要做的處理passdefhandle_request(self,request):cess_request(request)response=self.app.handle_request(request)cess_response(request,response)returnresponsedef__call__(self,environ,start_response):request二Request(environ)response=self.app.handle_request(request)returnresponse(environ,start_response)復(fù)制代碼其中add方法會實(shí)例化Middleware對象,該對象會將當(dāng)前的API類實(shí)例包裹起來。Middleware.handle_request方法其實(shí)就是在self.app.handle_reques前調(diào)用cess_reques方法處理request前的數(shù)據(jù)以及調(diào)用cess_response處理response后的數(shù)據(jù),而核心的調(diào)度邏輯,依舊交由API.handle_request方法進(jìn)行處理。這里的代碼可能會讓人感到疑惑,—call__^法和handle_request方法中都有self.app.handle_request(request)但其調(diào)用對象似乎不同?這個問題暫時放一下,先繼續(xù)完善代碼,然后再回來解釋。接著在api.py中為API創(chuàng)建middleware屬性以及添加新中間件的方法。classAPI(object):def__init__(self,templates_dir二"templates",static_dir="static"):#請求中間件,將api對象傳入self.middleware=Middleware(self)defadd_middleware(self,middleware_cls):#添加中間件self.middleware.add(middleware_cls)復(fù)制代碼隨后,在app.py中,自定義一個簡單的中間件,然后調(diào)用add_middleware方法將其添加。classSimpleCustomMiddleware(Middleware):defprocess_request(self,req):print處理request",req.url)defprocess_response(self,req,resp):print處理response",req.url)app.add_middleware(SimpleCustomMiddleware)復(fù)制代碼定義好中間件后,在請求調(diào)度時,就需要使用中間件,為了兼容靜態(tài)文件的情況,需要對css、jsing文件的請求路徑做一下兼容,在其路徑中加上/static前綴復(fù)制代碼緊接著,修改API的—call__兼容中間件和靜態(tài)文件,代碼如下。classAPI(object):def__call__(self,environ,start_response):path_info=environ["PATH_INFO"]static="/"+self.static_dir#以/stati開頭或中間件為空ifpath_info.startswith(static)ornotself.middleware:#"/static/index.css"只取/index.css/stati開頭只是用于判斷environ["PATH_INFO"]=path_info[len(static):]returnself.whitenoise(environ,start_response)returnself.middleware(environ,start_response)復(fù)制代碼至此,中間件的邏輯就完成了。但代碼中依舊有疑惑,Middleware類中的—call方法和handle_request方法其調(diào)用的self.app到底是誰?為了方便理解,這里一步步拆解。如果沒有添加新的中間件,那么請求的調(diào)度邏輯如下。#屬性映射關(guān)系A(chǔ)PI.middleware=MiddlewareAPI.middleware.app=API#調(diào)度邏輯API.__call__->middleware.__call__->self.app.handle_request->API.handle_request()復(fù)制代碼在沒有添加中間件的情況下,self.app其實(shí)就是API本身,所以middleware.__call中的self.app.handle_reques就是調(diào)用API.handle_request。如果添加了新的中間件,如上述代碼中添加了名為SimpleCustomMiddleware的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 《園區(qū)布局調(diào)研分析》課件
- 《流行性感冒講座》課件
- 四川省蒼溪縣2025屆初三下學(xué)期第一次調(diào)研物理試題試卷含解析
- 新疆烏魯木齊2025年高三下學(xué)期港澳臺入學(xué)考試語文試題含解析
- 山西省陽泉市平定縣2025年初三下學(xué)期4月聯(lián)合考試語文試題含解析
- 陜西省安康市名校2024-2025學(xué)年初三畢業(yè)班模擬考試(五)語文試題含解析
- 2025年中國支承市場調(diào)查研究報告
- 2025年中國搭扣鎖市場調(diào)查研究報告
- 2025年中國挖機(jī)玻璃數(shù)據(jù)監(jiān)測報告
- 關(guān)于醫(yī)療數(shù)據(jù)保護(hù)的跨領(lǐng)域培訓(xùn)項目計劃
- 工商管理實(shí)習(xí)周記十篇
- 幼兒園體育游戲活動評價表
- 醫(yī)療衛(wèi)生系統(tǒng)招聘《醫(yī)學(xué)基礎(chǔ)知識》備考題庫資料寶典(核心題版)
- 使用說明書儀表8530d技術(shù)手冊
- 星球版七年級地理上冊《海陸變遷》《火山噴發(fā)》實(shí)驗(yàn)說課 課件
- 五金工具零售規(guī)章制度
- GB/T 8312-2013茶咖啡堿測定
- GA/T 1217-2015光纖振動入侵探測器技術(shù)要求
- BA系統(tǒng)原理培訓(xùn)課件
- 五年級上冊數(shù)學(xué)試題- 五年級趣味數(shù)學(xué)社團(tuán)1(第五周活動安排:圖形面積(二))人教新課標(biāo) (無答案)
- 中醫(yī)發(fā)展史醫(yī)學(xué)課件
評論
0/150
提交評論