学习廖雪峰老师的,在Web框架这部分看了大致一个多礼拜,前面的知识学的不够扎实,由于比较复杂,因此在这里总结下,也算是巩固了。
先看下框架的调用代码:
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)
- 使用web.Application类创建aiohttp server——app,其中loop为Eventloop用来处理HTTP请求,middlewares为中间件,在这里用来记录日志并处理handler返回的数据为web.response对象,这里看下
response_factory
的代码
async def response_factory(app, handler): async def response(request): logging.info('Response handler...') #获取handler的返回值,根据返回值的不同类型进行处理 r = await handler(request) print(type(r)) 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): if r.startswith('redirect:'): return web.HTTPFound(r[9:]) resp = web.Response(body=r.encode('utf-8')) resp.content_type = 'text/html;charset=utf-8' return resp if isinstance(r, dict): template = r.get('__template__') if template is None: resp = web.Response(body=json.dumps(r, ensure_ascii=False, default=lambda o: o.__dict__).encode('utf-8')) resp.content_type = 'application/json;charset=utf-8' return resp else: resp = web.Response(body=app['__templating__'].get_template(template).render(**r).encode('utf-8')) resp.content_type = 'text/html;charset=utf-8' return resp if isinstance(r, int) and r >= 100 and r < 600: return web.Response(r) if isinstance(r, tuple) and len(r) == 2: t, m = r if isinstance(t, int) and t >= 100 and t < 600: return web.Response(t, str(m)) # default: resp = web.Response(body=str(r).encode('utf-8')) resp.content_type = 'text/plain;charset=utf-8' return resp return response
- 使用jinjia2模板来构建前端页面,这里我们暂时没有用到
- 注册处理url的handler,aiohttp中的add_route函数进行注册,我们这里使用
add_routes
对'handlers'模块的handler进行批量注册
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))
def add_routes(app, module_name): #找到'.'则返回其所在位置,否则返回-1 n = module_name.rfind('.') if n == (-1): #mod为包含module_name模块中全部属性和方法的list 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): #检查handler是否被@get或@post装饰 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)
这里出现了一个RequestHandler类,它具有call魔术方法,所以可以像调用函数一样调用其实例,这里RequestHandler类主要是对handler进行封装,获取request中传入的参数并传入handler中。
- add_static是用户处理如图片、js、css等静态资源的,仅供开发环境中方便使用,生产环境一般使用nginx或CDN之类的,这块我也还没有搞清楚,没办法梳理
最后把我的handler模块的代码贴出来,供参考:
from coroweb import get, postfrom aiohttp import web @get('/blog') async def handler_url_blog(request): body='' return body @post('/result') async def handler_url_result(*,user_email,request): body='Awesome: /blog
' return body @get('/greeting') async def handler_url_greeting(*,name,request): body='Awesome: /greeting %s
'%name return body @get('/input') async def handler_url_input(request): body='