<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Day 5 - 編寫Web框架 在正式開始Web開發前,我們需要編寫一個Web框架。 `aiohttp`已經是一個Web框架了,為什么我們還需要自己封裝一個? 原因是從使用者的角度來說,`aiohttp`相對比較底層,編寫一個URL的處理函數需要這么幾步: 第一步,編寫一個用`@asyncio.coroutine`裝飾的函數: ``` @asyncio.coroutine def 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.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處理函數了,可以極大地提高開發效率。 ## 參考源碼 [day-05](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>

                              哎呀哎呀视频在线观看