目录
1.aiohttp是什么
aiohttp是使用python+asyncio写的pythonHttp server/client异步框架,它可以运行在客户端和服务器端。
2.为什么使用aiohttp搭建后台
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框架。当然这是我的选择,每个人看重的点不同,框架只是一种选择,关键还是在于个人。
3.后台的搭建过程
(1)后台搭建
直接新建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函数中进行统一注册。这样就不需要每次写个函数都需要注册一次了
(2)aiohttp视图函数统一注册
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 decorate
(3)处理aiohttp中的跨域请求
aiohttp默认是不支持跨域请求的,但是可以使用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)
4.进一步的优化
经过上述步骤以后,后台函数就已经搭建完成,但是有一个问题就是,我们会发现随着后台函数越来越多,所有的后台函数都集中在一个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