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()
200
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():
    pass


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请求,文件上传,重定向等。

你可能感兴趣的:(python网络编程之aiohttp)