精彩文章免费看

python网络编程之aiohttp

aiohttp简介

aiohttp是一个建立在asyncio上的,既支持http又支持websocket的一个库。并且同时支持客户端和服务端。
官方文档: https://docs.aiohttp.org/en/stable/

这篇文章主要记述aiohttp的http的使用

aiohttp for http client

实现客户端一个简单的get请求

import aiohttp
import asyncio
async def get_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            print(resp.status)
            result = await resp.text()
            print("get {} length data from {}".format(len(result),url))
async def main():
    url = "http://python.org"
    await get_url(url)
await main()
get 50818 length data from http://python.org

aiohttp for http server

接下来,看如何使用aiohttp分别开放一个get和post的接口供客户端调用

from aiohttp import web
#如果需要使用装饰器来路由uri的话,需要创建RouteTableDef类的对象来获取一个装饰器
routes = web.RouteTableDef()
@routes.get("/get")
async def get_req(request):
    print("this is get req")
    return web.Response(text="Hello World")
@routes.post("/post")
async def post_req(request):
    print("this is post request")
    return web.Response(text="Post Success")
@routes.route("*", "/all")#支持GET/POST/PUT/DELETE
async def all_handler(request):
    print(request.method)
    print(type(request))
    print("handle all request")
    return web.Response(text="All")
def main():
if __name__ == '__main__':
    app = web.Application()
    #如果不使用装饰器的话, 添加如下代码
    # app.add_routes([web.get("/get", get_req),
    #                 web.post("/post",post_req)])
    #如果使用装饰器,则添加如下一行
    app.add_routes(routes)
    web.run_app(app)

客户端调用如下:

import aiohttp
import asyncio
async def get_request():
    async with aiohttp.ClientSession() as session:
        url = "http://0.0.0.0:8080/get"
        async with session.get(url) as resp:
            print("status:{}".format(resp.status))
            text = await  resp.text()
            print(text)
async def post_request():
    url = "http://0.0.0.0:8080/post"
    async  with aiohttp.ClientSession() as session:
        async with session.post(url, data={"a": 1}) as resp:
            print("status:{}".format(resp.status))
            res = await  resp.text()
            print(res)
async def main():
    await get_request()
    await  post_request()
if __name__ == '__main__':
    #asyncio.run(main())
    await main()
status:200
Hello World
status:200
Post Success
1、我们先定义一个接口方法(即请求处理函数),该方法接收一个参数request.类型为:aiohttp.web_request.Request
2、然后创建一个Application的应用实例
3、把接口方法和对应的path注册到Application。有两种注册方法:

  • app.add_routes([web.get("/path", request_handler)])
  • 使用装饰器,在request_handler方法上面加上@routes.get/post("/path")
    get/post对应相应http请求GET和POST,当然也还有put和delete。同理。
    如果想要使该接口即支持post请求也支持get请求,@routes.route("*", "/path")
    4、最后运行Application实例。
  • 1、客户端的每一次请求通过session完成,先创建一个ClientSession
    2、然后使用调用session的get/post/put/delete方法去完成相应的请求,返回一个Response对象
    3、通过返回的Response对象,我们可以获取返回消息(通过text()方法)

    参数传递与参数获取

    上面我们实现了简单的服务器的收发数据以及客户端的请求数据。接下来,看一下,request和post请求中的参数如何获取。以对一个灯的操作为例。

    #server.py
    import asyncio
    from aiohttp import web
    import json
    routes = web.RouteTableDef()
    #接受uri中可传变量
    @routes.get("/light/on/{addr}")
    async def turn_on(request):
        #通过match_info可以获取path中的变量信息
        addr = request.match_info['addr']
        await asyncio.sleep(1) #假装去处理事情
        print("turn on the light:{}".format(addr))
        #返回一个字符串
        return web.Response(text="success")
    @routes.get("/light/off/{addr}")
    async def turn_off(request):
        #通过match_info可以获取path中的变量信息
        addr = request.match_info['addr']
        await asyncio.sleep(1) #假装去处理事情
        print("turn off the light:{}".format(addr))
        #返回一个json数据
        data = {"result":True}
        return web.json_response(data = data)
    #熟悉post数据的读取
    @routes.post("/light/set_color/{addr}")
    async  def set_color(request):
        addr = request.match_info['addr']
        if request.can_read_body:
            print("has body")
            #如果传递的是data=,则可以使用通过content读取
            # body = await request.content.read() #读取post的数据内容,content是一个StreamReader
            # body = str(body,encoding ='utf-8') #将读取到的byte数据转成字符串
            # body_json = json.loads(body) #把字符串转换成json
            body_json = await request.json()
            print("get post data:{}".format(body_json))
            print(type(body_json))
            assert body_json["color"]
            color = body_json["color"]
            print("set color to:{} for light:{}".format(color,addr))
            data = {"result":True,"color":color}
            return web.json_response(data)
        else:
            data = {"result":False,"err":"invalid params"}
        return web.json_response(data)
    #带参数查询的get请求
    #此函数接收一个参数name
    @routes.get("/light/list")
    async def get_online_lights(request):
        #获取查询参数,request.query是一个MultiDictProxy对象,我们可以向字典一样操作它
        params = request.query
        name = params.get("name") #使用get方法,如果不存在该key值,会返回None
        if name is None:
            err = {"result":False,"err":"invalid params"}
            return web.json_response(err)
        print("query all light for {}".format(name))
        light_list = ["00-12-4b-00-54-23-09","00-12-4b-00-54-24-01"]
        data = {"light_list":light_list,"name":name}
        return web.json_response(data)
    def main():
        app = web.Application()
        app.add_routes(routes)
        web.run_app(app)
    if __name__== "__main__":
        main()
    
    #client.py
    import json
    import aiohttp
    import asyncio
    import json
    import ujson
    async  def turn_on_light(addr):
        async with aiohttp.ClientSession() as session:
            url = "http://0.0.0.0:8080/light/on/"+addr
            async with session.get(url) as resp:
                if resp.status == 200:
                    result = await resp.text()
                    print("turn on result is:{}".format(result))
    async  def turn_off_light(addr):
        async with aiohttp.ClientSession() as session:
            url = "http://0.0.0.0:8080/light/off/"+addr
            async with session.get(url) as resp:
                if resp.status == 200:
                    result = await resp.json() #获取返回的json数据
                    print("turn off result is:{}".format(result["result"]))
    async  def set_color(addr):
        async with aiohttp.ClientSession() as session:
            url = "http://0.0.0.0:8080/light/set_color/"+addr
            data = {"color":"f0f0f0"}
            headers = {"content-type":"application/json"}
            #可以直接传递json数据,需要使用json=而不是data=
            #默认情况下会话(session)使用Python标准库里的json模块解析json信息。
            # 但还可使用其他的json解析器。可以给ClientSession指定json_serialize参数来实现
            async with session.post(url,json=data,headers=headers) as resp:
                if resp.status == 200:
                    result = await resp.json()
                    print("set color result is:{}".format(result))
                else:
                    print(resp.status)
    async  def query_lights_list():
        async with aiohttp.ClientSession() as session:
            url = "http://0.0.0.0:8080/light/list"
            params = {"name":"H.H"}
            headers = {"content-type":"application/json"}
            async with session.get(url,params=params)as resp:
                if resp.status == 200:
                    result = await resp.text()
                    print("query light list is:{}".format(result))
                else:
                    print(resp.status)
    async  def main():
        light_addr = "00-12-4b-00-54-23-09"
        await  turn_on_light(light_addr)
        await  turn_off_light(light_addr)
        await  set_color(light_addr)
        await query_lights_list()
    if __name__ == "__main__":
        asyncio.run(main())
    

    上面的例子分别涉及到了uri存在变量,post的内容获取,get请求的参数的传递与获取以及json数据的返回。基本上能够涉及到常用的网络请求。相关地方都有注释说明。所有代码均在pycharm中运行通过,python版本:3.8.5。官方文档里面还有很多介绍,很全面,比如Http表格的post请求,文件上传,重定向等。

    最后编辑于:2021-11-16 00:32