Day5:编写Web框架
前面完成了数据库的部署与操作封装,现在开始进入到服务器方面的工作。我们前面提到了,在本项目中我们引入了异步框架aiohttp,并且他自身提供了一个服务器。aiohttp已经是一个框架了,为什么我们还要再自己实现呢?
原因在于,从框架的使用者的角度来说,aiohttp还是相对比较底层,想要在使用框架时所需要的代码更简洁,就需要我们在aithttp的基础上增添一些其他的公用的功能,封装出更高级的Web框架。Web框架的设计是完全从使用者出发,目的是让框架使用者编写尽可能少的代码。
编写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'))
这些重复的工作可以由框架完成,我们的目标也是如此。
接下来在coroweb.py中编写框架代码
Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。
URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。
建议:
1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
2、在做数据查询时,建议用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是一个装饰器。这样,一个函数通过@get()的装饰就附带了URL信息。
@post与@get定义类似。
def post(path):
'''
Define decorator @post('/path')
'''
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
return func(*args, **kw)
wrapper.__method__ = 'POST'
wrapper.__route__ = path
return wrapper
return decorator
定义RequestHandler
URL处理函数不一定是一个coroutine,因此我们用RequestHandler()来封装一个URL处理函数。
RequestHandler是一个类,创建的时候定义了_call_()方法,因此可以将其实例视为函数。
RequestHandler目的就是从URL函数中分析其需要接收的参数,从request中获取必要的参数,调用URL函数,然后把结果转换为web.Response对象,这样,就完全符合aiohttp框架的要求
#运用inspect模块,创建几个函数用以获取URL处理函数与request参数之间的关系
def get_required_kw_args(fn): #收集没有默认值的命名关键字参数
args = []
params = inspect.signature(fn).parameters #inspect模块是用来分析模块,函数
for name, param in params.items():
if str(param.kind) == 'KEYWORD_ONLY' and param.default == inspect.Parameter.empty:
args.append(name)
return tuple(args)
def get_named_kw_args(fn): #获取命名关键字参数
args = []
params = inspect.signature(fn).parameters
for name,param in params.items():
if str(param.kind) == 'KEYWORD_ONLY':
args.append(name)
return tuple(args)