標籤: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對象,可以是str、bytes或dict。
如果希望渲染模板,我們可以這麼返回一個dict:
return { ‘__template__‘: ‘index.html‘, ‘data‘: ‘...‘}
因此,Web架構的設計是完全從使用者出發,目的是讓使用者編寫儘可能少的代碼。
編寫簡單的函數而非引入request和web.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中加入middleware、jinja2模板和自註冊的支援:
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架構