目录
1.aiohttp是什么
2.为什么使用aiohttp搭建后台
3.后台的搭建过程
(1)后台搭建
(2)aiohttp视图函数统一注册
(3)处理aiohttp中的跨域请求
4.进一步的优化
aiohttp是使用python+asyncio写的pythonHttp server/client异步框架,它可以运行在客户端和服务器端。
python的web框架很多,可以用来做restful接口的后台框架也有很多,比较著名的就是django和flask了,它们分别有对应的restful扩展,用起来也很方便,那么我为什么没有选择它们而是选择了aiohttp呢?以下几点是我选择原因
(1)aiohttp是异步框架。与Django和flask相比,它是异步框架,意味着更快的速度和更高的效率,并且使用原生的asyncio写的。
(2)良好的websocket支持。aiohttp是自带websocket,并且也是异步的。对于Django而言可以使用channels实现异步websocket,但是配置过程比较繁琐,而flask则还不支持在视图函数中异步回调,可能需要使用celery任务队列来完成并发,配置更加繁琐,并且原生也不支持websocket,需要安装flask-websocketio
(3)客户/服务端均可用。与tornado相比,aiohttp既可以用作后台接口,也可以用作发送异步请求,这就保证了整个后台python的一致性。
(4)冗余较少,使用方便。由于我采用的是前后端分离,所以flask和Django显得有些冗余,使用aiohttp来搭建后台更简洁方便。
综上几点,我选择了使用aiohttp作为搭建后台的python框架。当然这是我的选择,每个人看重的点不同,框架只是一种选择,关键还是在于个人。
直接新建python文件即可,以下是我的部分代码
#!/usr/bin/python3.6.5 # -*- coding: utf-8 -*- # Time : 2020/7/5 14:33 # Author : He # Github : https://github.com/JustKeepSilence import os import json import asyncio from functools import wraps from multiprocessing import Process from typing import Optional, Callable import aiohttp_cors from util import db from aiohttp import web from util import utils as uu from util.data_structure import * from download_spider.main import * app = web.Application() # 登陆界面的接口 @method_dec("/login") async def login(request): try: request_data = await request.json() username = request_data.get("username") # 获取用户名 password = request_data.get("password") # 获取密码 query_data = User(*(db.get_data(f"select * from user_view where username='{username}'")[0])) if query_data.password == password: return web.json_response({"msg": "登陆成功!", "code": 200, "name": username, "token": query_data.usertoken, "avatar": query_data.user_avatar}) else: return web.json_response({"msg": "密码错误", "code": 500}) except (IndexError, Exception): import traceback return web.json_response({"msg": "用户名错误" + traceback.format_exc(), "code": 500}) if __name__ == "__main__": cors = aiohttp_cors.setup(app, defaults={ "*": aiohttp_cors.ResourceOptions( allow_credentials=True, expose_headers="*", allow_headers="*", ) }) for parm in _PARAM_LISTS: if parm["register"]: app.router.add_route(parm["method"], parm["name"], parm["func"]) for route in list(app.router.routes()): cors.add(route) web.run_app(app, host="127.0.0.1", port=5000)其中aiohttp_cors是用来处理跨域请求,method_dec是装饰器函数,用来获得路由的url和请求方式以便在main函数中进行统一注册。这样就不需要每次写个函数都需要注册一次了
aiohttp支持flask或者是Django式的路由请求写法。但是为了支持跨域请求(后面会提到)一般建议使用Django式的写法,即每次写完都去注册一下路由,具体可以参见官方文档https://docs.aiohttp.org/en/stable/web_quickstart.html,但是这样会很烦躁,所以我写了一个装饰器函数,记录请求的url和方式,之后在main函数中统一注册,这样就不需要每次新增接口都去注册,而只需要在接口函数上加上该装饰器,就会在main函数中自动注册
def method_dec(url: str, method: Optional[str] = "post", register: Optional[bool] = True,) -> Callable: """函数的装饰器函数,便于统一注册""" def decorate(func: Callable): @wraps(func) def inner_func(*args, **kwargs): _PARAM_LISTS.append({"name": url, "method": method, "register": register, "func": func}) return inner_func() return decorateaiohttp默认是不支持跨域请求的,但是可以使用aiohttp_cors模块的解决这个问题,安装完成之后,写入以下代码,即可以解决请求的跨域请求问题。
cors = aiohttp_cors.setup(app, defaults={ "*": aiohttp_cors.ResourceOptions( allow_credentials=True, expose_headers="*", allow_headers="*", ) }) for parm in _PARAM_LISTS: if parm["register"]: app.router.add_route(parm["method"], parm["name"], parm["func"]) for route in list(app.router.routes()): cors.add(route)经过上述步骤以后,后台函数就已经搭建完成,但是有一个问题就是,我们会发现随着后台函数越来越多,所有的后台函数都集中在一个python文件当中,会显得很臃肿,并且不便于管理,所以我们可以进行进一步的优化,将不同页面的后台接口写在不同的python文件中,然后再一起导入,这样就方便管理,并且整个项目的结构也更清晰。例如我们将登陆界面的接口单独写在login.py中,代码如下:
#!/usr/bin/python3.6.5 # -*- coding: utf-8 -*- # Time : 2020/9/17 8:35 # Author : He # Github : https://github.com/JustKeepSilence # 登陆接口的后台函数,python3.8以上 import hashlib import datetime from aiohttp import web from util import db # 异步操作数据库 from util.decorator import method_dec # 装饰器 from util.data_structure import User, UserRole # 数据库表的结构 __all__ = ('login', 'get_user_info') # 登陆界面的接口 @method_dec("/login") async def login(request): """用户登陆 `username`: 用户民 `password`: 密码 `return`: dict{code: int, msg: str, username: str, token: str, avatar: str} """ try: request_data = await request.json() username = request_data.get("username") # 获取用户名 password = request_data.get("password") # 获取密码 # 每次重新登陆的时候都会根据用户名和当前时间去重新生成特定的token,这样保证了用户只能单点登陆 user_token = hashlib.md5((username[:-2] + str(datetime.datetime.now())).encode("utf-8")).hexdigest() query_data = [User(*item) async for item in await db.get_data(f"select * from user_view where username='{username}'")] # 从数据库获取用户信息 if query_data[0].password == password: # 如果用户输入的密码正确 await db.change_data(f"update user_cfg set {user_token=} where {username=}") # 更新用户的token return web.json_response({"msg": "登陆成功!", "code": 200, "name": username, "token": user_token, "avatar": query_data[0].user_avatar}) else: return web.json_response({"msg": "密码错误", "code": 500}) except (IndexError, Exception): return web.json_response({"msg": "用户名错误", "code": 500}) @method_dec("/get_user_info", 'get') async def get_user_info(request): """根据token获取用户信息 `token`: 用户的token信息 `return`: dict{code: int, data: dict{user_role: str}} """ token = request.headers.get('Token') # 获取token response_data = [{"role": item.role_name, "userName": item.username} for item in [UserRole(*item) async for item in await db.get_data( f"select username, role_name from user_view where usertoken='{token}'")]] return web.json_response({"data": response_data[0], "msg": "success", "code": 200})之后再将login.py中的函数导入到web.py中
#!/usr/bin/python3.6.5 # -*- coding: utf-8 -*- # Time : 2020/7/5 14:33 # Author : He # Github : https://github.com/JustKeepSilence import aiohttp_cors from api.login import * from util.config import * # 导入PARAM_LIST app = web.Application() sio.attach(app) if __name__ == "__main__": cors = aiohttp_cors.setup(app, defaults={ "*": aiohttp_cors.ResourceOptions( allow_credentials=True, expose_headers="*", allow_headers="*", ) }) for parm in PARAM_LIST: if parm["register"]: app.router.add_route(parm["method"], parm["url"], parm["func"]) for route in list(app.router.routes()): try: cors.add(route) except ValueError: pass web.run_app(app, host=IP, port=PORT)到这里后台就已经搭建完成。至于完成的代码大家可以去我的github上查看,如何使用websocket我也会在后面的文章中分享给大家~
项目Github地址:https://github.com/JustKeepSilence/DownLoad