python 編寫Web架構

來源:互聯網
上載者:User

標籤:oct   mat   symbol   集中   als   continue   一個   request   類比   

在正式開始Web開發前,我們需要編寫一個Web架構。

aiohttp已經是一個Web架構了,為什麼我們還需要自己封裝一個?

原因是從使用者的角度來說,aiohttp相對比較底層,編寫一個URL的處理函數需要這麼幾步:

第一步,編寫一個用@asyncio.coroutine裝飾的函數:

@asyncio.coroutinedef handle_url_xxx(request):    pass

第二步,傳入的參數需要自己從request中擷取:

url_param = request.match_info[‘key‘]query_params = parse_qs(request.query_string)

最後,需要自己構造Response對象:

text = render(‘template‘, data)return web.Response(text.encode(‘utf-8‘))

這些重複的工作可以由架構完成。例如,處理帶參數的URL/blog/{id}可以這麼寫:

@get(‘/blog/{id}‘)def get_blog(id):    pass

處理query_string參數可以通過關鍵字參數**kw或者命名關鍵字參數接收:

@get(‘/api/comments‘)def api_comments(*, page=‘1‘):    pass

對於函數的傳回值,不一定是web.Response對象,可以是strbytesdict

如果希望渲染模板,我們可以這麼返回一個dict

return {    ‘__template__‘: ‘index.html‘,    ‘data‘: ‘...‘}

因此,Web架構的設計是完全從使用者出發,目的是讓使用者編寫儘可能少的代碼。

編寫簡單的函數而非引入requestweb.Response還有一個額外的好處,就是可以單獨測試,否則,需要類比一個request才能測試。

@get和@post

要把一個函數映射為一個URL處理函數,我們先定義@get()

def get(path):    ‘‘‘    Define decorator @get(‘/path‘)    ‘‘‘    def decorator(func):        @functools.wraps(func)        def wrapper(*args, **kw):            return func(*args, **kw)        wrapper.__method__ = ‘GET‘        wrapper.__route__ = path        return wrapper    return decorator

這樣,一個函數通過@get()的裝飾就附帶了URL資訊。

@post@get定義類似。

定義RequestHandler

URL處理函數不一定是一個coroutine,因此我們用RequestHandler()來封裝一個URL處理函數。

RequestHandler是一個類,由於定義了__call__()方法,因此可以將其執行個體視為函數。

RequestHandler目的就是從URL函數中分析其需要接收的參數,從request中擷取必要的參數,調用URL函數,然後把結果轉換為web.Response對象,這樣,就完全符合aiohttp架構的要求:

class RequestHandler(object):    def __init__(self, app, fn):        self._app = app        self._func = fn        ...    @asyncio.coroutine    def __call__(self, request):        kw = ... 擷取參數        r = yield from self._func(**kw)        return r

再編寫一個add_route函數,用來註冊一個URL處理函數:

def add_route(app, fn):    method = getattr(fn, ‘__method__‘, None)    path = getattr(fn, ‘__route__‘, None)    if path is None or method is None:        raise ValueError(‘@get or @post not defined in %s.‘ % str(fn))    if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):        fn = asyncio.coroutine(fn)    logging.info(‘add route %s %s => %s(%s)‘ % (method, path, fn.__name__, ‘, ‘.join(inspect.signature(fn).parameters.keys())))    app.router.add_route(method, path, RequestHandler(app, fn))

最後一步,把很多次add_route()註冊的調用:

add_route(app, handles.index)add_route(app, handles.blog)add_route(app, handles.create_comment)...

變成自動掃描:

# 自動把handler模組的所有合格函數註冊了:add_routes(app, ‘handlers‘)

add_routes()定義如下:

def add_routes(app, module_name):    n = module_name.rfind(‘.‘)    if n == (-1):        mod = __import__(module_name, globals(), locals())    else:        name = module_name[n+1:]        mod = getattr(__import__(module_name[:n], globals(), locals(), [name]), name)    for attr in dir(mod):        if attr.startswith(‘_‘):            continue        fn = getattr(mod, attr)        if callable(fn):            method = getattr(fn, ‘__method__‘, None)            path = getattr(fn, ‘__route__‘, None)            if method and path:                add_route(app, fn)

最後,在app.py中加入middlewarejinja2模板和自註冊的支援:

app = web.Application(loop=loop, middlewares=[    logger_factory, response_factory])init_jinja2(app, filters=dict(datetime=datetime_filter))add_routes(app, ‘handlers‘)add_static(app)
middleware

middleware是一種攔截器,一個URL在被某個函數處理前,可以經過一系列的middleware的處理。

一個middleware可以改變URL的輸入、輸出,甚至可以決定不繼續處理而直接返回。middleware的用處就在於把通用的功能從每個URL處理函數中拿出來,集中放到一個地方。例如,一個記錄URL日誌的logger可以簡單定義如下:

@asyncio.coroutinedef logger_factory(app, handler):    @asyncio.coroutine    def logger(request):        # 記錄日誌:        logging.info(‘Request: %s %s‘ % (request.method, request.path))        # 繼續處理請求:        return (yield from handler(request))    return logger

response這個middleware把傳回值轉換為web.Response對象再返回,以保證滿足aiohttp的要求:

@asyncio.coroutinedef response_factory(app, handler):    @asyncio.coroutine    def response(request):        # 結果:        r = yield from handler(request)        if isinstance(r, web.StreamResponse):            return r        if isinstance(r, bytes):            resp = web.Response(body=r)            resp.content_type = ‘application/octet-stream‘            return resp        if isinstance(r, str):            resp = web.Response(body=r.encode(‘utf-8‘))            resp.content_type = ‘text/html;charset=utf-8‘            return resp        if isinstance(r, dict):            ...

有了這些基礎設施,我們就可以專註地往handlers模組不斷添加URL處理函數了,可以極大地提高開發效率。

http://www.jiuaidushu.com/cpgjhegfgncpcfefdfcfdjdccfdjdfcfefdfcfdjdjcfdjedcfefdfcfecdacfdiegcfefdfcfebdecfebdjcfefdecfeceecfeceg/

python 編寫Web架構

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.