1.aiohttp后台的搭建

目录

1.aiohttp是什么

2.为什么使用aiohttp搭建后台

3.后台的搭建过程

(1)后台搭建

(2)aiohttp视图函数统一注册

(3)处理aiohttp中的跨域请求

4.进一步的优化




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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值