<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] ### 引子 在正式開始Web開發前,我們需要編寫一個Web框架。 `aiohttp`已經是一個`Web`框架了,為什么我們還需要自己封裝一個? 原因是從使用者的角度來說,`aiohttp`相對比較底層,編寫一個URL的處理函數需要這么幾步: ### 編寫函數 第一步,編寫一個用`@asyncio.coroutine`裝飾的函數: ~~~ @asyncio.coroutine def handle_url_xxx(request): pass ~~~ ### 從 request 中取出參數 第二步,傳入的參數需要自己從`request`中獲取: ~~~ url_param = request.match_info['key'] query_params = parse_qs(request.query_string) ~~~ ### 構造 Response 對象 最后,需要自己構造`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.coroutine def 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.coroutine def 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處理函數了,可以極大地提高開發效率。 [參考源碼](https://github.com/michaelliao/awesome-python3-webapp/tree/day-05)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看