1. 程式人生 > >徒手擼出一個類Flask微框架(二)路由及路由註冊的演變

徒手擼出一個類Flask微框架(二)路由及路由註冊的演變

pass nac 等價 themes 字典 lac found round wap

路由的基本概念:

根據不同的訪問路徑調用不同的方法或者類


from webob import Response,Request,dec
from wsgiref.simple_server import make_server,demo_app
def index(request:Request):
    res = Response()
    res.body = 'index.html'.encode()
    return res
def showpython(request:Request):
    res = Response()
    res.body = 'show_python'.encode()
    return res
def notfound():
    res = Response()
    res.status_code = 404
    return res
@dec.wsgify
def app(request:Request):
    if request.path == '/':
        return index(request)
    elif request.path == '/python':
        return showpython(request)
    else:
        return notfound()

如果用字典描述它是否更好

如果找不到則調用notfound

@dec.wsgify
def app(request:Request):
    ROUTE.get(request.path,notfound)(request)
if __name__ == "__main__":
    ip = '127.0.0.1'
    port = 9999
    server = make_server(ip,port,app)
    server.serve_forever()
    server.server_close()



實現註冊

這樣的話就可以將路由表設置為空

用這個路由的話起碼保證了已經註冊過了


def index(reques:Request):
    res = Response()
    res.body = 'index.html'.encode()
    return res
def showpython(reques:Request):
    res = Response()
    res.body = 'python.html'.encode()
    return res
def notfound():
    res = Response()
    res.status_code = 404
    res.body = 'Not Found'.encode()
    return res
# Route_Table
ROUTERTABLE = {}
def register(path,handler):
    ROUTERTABLE[path] = handler
register('/',index)
register('/python',showpython)
@wsgify
def app(request:Request):
    ROUTE.get(request.path, notfound)(request)

路由的封裝

class APP:
    def index(self,request:Request):
        res = Response()
        res.body = 'hello'.encode()
        return res
    def showpython(self,request:Request):
        res = Response()
        res.body = 'show python'.encode()
        return res



分析好處與瓶頸分別在哪裏

對一個web框架來講,這些方法都應該是用戶自己去定義,而並非是框架內去完成的,因為框架是提供用戶去使用,所以是由使用者去實現

那麽對於對於哪些是放在外面,合理的規劃如下:

class APPlication:
    def __init__(self,env,start_response):
        pass
    def notfound(request:Request):
        res = Response
        res.status_code = 404
        res.body = '404'.encode()
        return res
    #路由表
    ROUTERTABLE = {}
    def register(path,handler):
        ROUTERTAB[path] = handler
    @dec.wsgify
    def app(request:Request):
        return ROUTETABLE.get(request.path,notfound)(request)
#在外部定義執行的函數
def index(self,request:Request):
    res = Response()
    res.body = 'hello'.encode()
    return res



類的實例化

如果通過類進行定義那麽必須實例化,因為需要__init__的支撐

對於一個類中要麽在init中實現,要麽在__call__ 中實現


__init__:如果定義在init中,實例化後則是被寫死,顯然不符當前調用的靈活性,相當於在調用缺省的兩個函數

class APPlication:
    def __init__(self,env,start_response):

__call__:如果在__call__中定義,顯然調用和傳參是比較靈活的

思考一個問題:__call__是如何調用的

改進

對於server來講


server = make_server(ip,port,app)

一定是看到兩個參數調用,因為有裝飾器被裝飾一定保證這個__call__ 一定被轉化成wsgify

class Application:
    def notfound(request:Request):
        res = Response()
        res.status_code = 404
        res.body = 'not found~~'.encode()
        return res
    ROUTETABLE = {}
    # 註冊
    def register(path,handler):
        ROUTETABLE[path] = handler
    @dec.wsgify
    def __call__(self,request:Request):
        return ROUTETABLE.get(request.path, notfound)(request)
def index(request:Request):
    res = Response
    res.body = 'hh'.encode()
    return res


這樣的話通過裝飾器進行修飾之後,變成了我們想要的方式

最開始的__call__方法必須滿足兩個參數 request 和 response 必須保證進出,那如果通過wsgify的裝飾之後,那麽確保這個__call__變成了接口,滿足了接口定義的要求

技術分享圖片

這裏request就是業務上的第一個參數

通過request傳進之後,返回一個返回值,可以達到最後要求,只不過是用裝飾器來完成

返回值需要通過裝飾器 start_response 保證最後是一個可叠代對象

簡單的來講,就是將這個例子:

@wsgify
def myfunc(request:Request):
    return Response('hey here')

轉為了另一個例子:

    @wsgify
    #def __call__(self, *args, **kwargs):
    def __call__(self,request:Request):
        return ROUTER.get(request.path, self.notfound)(request)

通過__call__ 方法可以直接拿來做函數使用,這就省去了一些不必要的麻煩

添加異常,避免訪問出現問題

@dec.wsgify
def __call__(self,request:Request):
    try:
        print(request)        #將request 傳遞給了index(request)
        return cls.ROUTE[request.path](request)
    except:
        return self.notfound(request)


加入註冊

傾向於使用類方法,將註冊函數加入到類中

先到server中由其交給業務函數管理,業務函數是用戶根據自己的需求自己填寫內容

from webob import Response,Request,dec,exc
from wsgiref.simple_server import make_server,demo_app
import re
class Application:
    def notfound(self,request:Request):
        res = Response()
        res.status_code = 404
        res.body = 'hel'.encode()
        return res
    ROUTE = { }
    @classmethod
    def register(cls,path,handler):
        cls.ROUTE[path] = handler
    @dec.wsgify
    def __call__(self, request:Request):
        return self.ROUTE.get(request.path, self.notfound)(request)
def index(request:Request):
    res = Response()
    res.status_code = 200
    res.body = 'hhh'.encode()
    return res
Application.register('/',index)
if __name__ == "__main__":
    ip = '127.0.0.1'
    port = 9999
    server = make_server(ip,port,Application())
    server.serve_forever()
    server.server_close()



exc 異常模塊及狀態碼相關處理

exc主要提供了異常模塊及狀態碼相關的處理功能

導入模塊

from webob import Response,Request,dec,exc

查看源碼:

看到源碼內定義了幾乎所有的HTTP_Code 每個都是通過單獨類來實現,並繼承自HTTPClientxxxx

比如:200的類

class HTTPOk(WSGIHTTPException):
"""
Base class for the 200's status code (successful responses)
code: 200, title: OK
"""
    code = 200
    title = 'OK'


再比如404的類

class HTTPNotFound(HTTPClientError):
    """
    subclass of :class:`~HTTPClientError`
    This indicates that the server did not find anything matching the
    Request-URI.
    code: 404, title: Not Found
    """
    code = 404
    title = 'Not Found'
    explanation = ('The resource could not be found.')


發現每個狀態碼的類都是子類,繼續跟進父類查看:

查看HTTPClientError

class HTTPClientError(HTTPError):
    code = 400
    title = 'Bad Request'
    explanation = ('The server could not comply with the request since\r\n'
    'it is either malformed or otherwise incorrect.\r\n')
也是一個子類,再次跟進HTTPError
class HTTPError(WSGIHTTPException): 
WSGIHTTPException 代碼如下
class WSGIHTTPException(Response, HTTPException):
## You should set in subclasses:
# code = 200
# title = 'OK'
# explanation = 'why this happens'
# body_template_obj = Template('response template')
    code = 500
    title = 'Internal Server Error'
    explanation = ''
    body_template_obj = Template('''    ${explanation}<br /><br />
    ${detail}
    ${html_comment}
    ''')

它的子類將其方法進行逐層覆蓋,並返回其實還是調用的這個方法

並調用了wsgi的接口函數wsgi_response

最後實際調用的是__call__並傳遞兩個參數,environ 請求的所有信息 以及 start_response

所謂http code就是封裝一個編號並返回response body的內容

exc是多繼承,繼承了response,將一個缺省的body可以傳遞

如果出現某些問題則調用異常,這個異常本身繼承resopnse進行raise出去

所以直接調用exc.類名就可以了

@dec.wsgify
def __call__(self,request:Request):
    try:
        print(request)
        return self.ROUTE[request.path](request)
    except:
        return exc.HTTPNotFound('hahaha')


所以,只要拋異常就是沒有正常獲取,直接拋異常即可




改進註冊過程,使用裝飾器進行包裝

我們看到效果,當註冊路由的時候,使用Application.reg功能進行調用,但是明顯是不靈活的

能否像其他框架直接在方法或者函數上面寫明要指定的路由路徑呢

比如

@xxxxx.register('/',index)
def index(request:Response):
    res = Response()
    res.body = 'index'.encode()
    return res
目前來看,只能使用裝飾器來完成當前效果了
等價式:--> index = app.reg('/')(index)
    @dec.wsgify
    def __call__(self,request:Request):
        try:
            print(request)
            return self.ROUTE[request.path](request)
            # return self.ROUTE.get(request.path, self.notfound)(request)
        except:
            return exc.HTTPNotFound('hahaha')
# @Application.reg('/',index)  帶參裝飾器   --> Application() -->  func(path)(index)
@Application.reg('/')
def index(request:Response):
    res = Response()
    res.body = 'index'.encode()
    return res



路由主要是解決了後端問題

完整如下:


from webob import Response,Request,exc
from webob.dec import wsgify
from wsgiref.simple_server import make_server,demo_app
import re
# @wsgify
# def index(request:Request):
#     res = Response()
#     res.body = 'index.html'.encode()
#     return res
class Application:
    ROUTER_TABLE = {}
    @classmethod
    def register(cls,path):
        def wapper(handler):
            cls.ROUTER_TABLE[path] = handler
            print(cls.ROUTER_TABLE)
            return handler
        return wapper
    @wsgify
    def __call__(self, request):
        try:
            print(request.path)
            print(request)
            print(self.ROUTER_TABLE[request.path])
            return self.ROUTER_TABLE[request.path](request)
        except:
            raise exc.HTTPNotFound('not funod~~')
@Application.register('/')  # --> index = app.reg('/')(index)
def index(request:Request):
    res = Response()
    res.body = 'index.html'.encode()
    return res
@Application.register('/python')  # --> index = app.reg('/')(index)
def showpython(request:Request):
    res = Response()
    res.body = 'python'.encode()
    return res
if __name__ == "__main__":
    ip = '127.0.0.1'
    port = 9999
    server = make_server(ip,port,Application())
    server.serve_forever()
    server.server_close()









徒手擼出一個類Flask微框架(二)路由及路由註冊的演變