在python后台——asyncio,aiohttp入门教程,多进程+asyncio文章中,我们介绍了asyncio,aiohttp的入门知识,现在我们这里详细介绍一下aiohttp

参考文档:https://www.bookstack.cn/read/aiohttp-chinese-documentation/aiohttp%E6%96%87%E6%A1%A3-ServerTutorial.md

aiohttp.web建立在这几个概念之上: 应用(application),路由(router),请求(request)和响应(response)。

底层服务器

aiohttp是基于asyncio来实现的。如果不是需要和其他的异步进程一块使用,我们一般用不到这么底层的实现方法。

import asyncio
from aiohttp import webasync def handler(request):return web.Response(text="OK")async def main(loop):server = web.Server(handler)await loop.create_server(server, "127.0.0.1", 8080)print("======= Serving on http://127.0.0.1:8080/ ======")# pause here for very long time by serving HTTP requests and# waiting for keyboard interruptionawait asyncio.sleep(100*3600)loop = asyncio.get_event_loop()try:loop.run_until_complete(main(loop))
except KeyboardInterrupt:pass
loop.close()

创建应用程序

from aiohttp import web
app = web.Application()
web.run_app(app, host='127.0.0.1', port=8080)

添加路由

from views import index
def setup_routes(app):app.router.add_get('/', index)from aiohttp import web
from routes import setup_routes
app = web.Application()
setup_routes(app)
web.run_app(app, host='127.0.0.1', port=8080)

可变性路由

使用Request.match_info来读取路由中变量

async def variable_handler(request):return web.Response(text="Hello, {}".format(request.match_info.get('name', "Anonymous")))
resource = app.router.add_resource('/{name}')
resource.add_route('GET', variable_handler)

默认情况下,每个可变部分所使用的正则表达式是[^{}/]+

当然你也可以自定义正则表达式{标识符: 正则}:

resource = app.router.add_resource(r'/{name:\d+}')

Flask风格路由修饰器

routes = web.RouteTableDef()
@routes.get('/get')
async def handle_get(request):...
@routes.post('/post')
async def handle_post(request):...
app.router.add_routes(routes)

资源视图

所有在路由中注册的资源都可使用UrlDispatcher.resources()查看:

for resource in app.router.resources():print(resource)

同样,有name的资源可以用UrlDispatcher.named_resources()来查看:

for name, resource in app.router.named_resources().items():print(name, resource)

0.21版本后请使用UrlDispatcher.named_routes() / UrlDispatcher.routes() 来代替 UrlDispatcher.named_resources() / UrlDispatcher.resources() 。

抽象路由类

路由(router)是一个可插拔的部分: 用户可以从头开始创建一个新的路由库,不过其他的部分必须与这个新路由无缝契合才行。
AbstractRouter只有一个必须(覆盖)的方法: AbstractRouter.resolve()协程方法。同时必须返回AbstractMatchInfo实例对象。
如果所请求的URL的处理器发现AbstractMatchInfo.handler()所返回的是web处理器,那AbstractMatchInfo.http_exception则为None。
否则的话AbstractMatchInfo.http_exception会是一个HTTPException实例,如404: NotFound 或 405: Method Not Allowed。如果调用AbstractMatchInfo.handler()则会抛出http_exception。

class aiohttp.abc.AbstractRouter
   此类是一个抽象路由类,可以指定aiohttp.web.Application中的router参数来传入此类的实例,使用aiohttp.web.Application.router来查看返回.
   coroutine resolve(request)
      URL处理方法。该方法是一个抽象方法,需要被继承的类所覆盖。
      参数:
         request - 用于处理请求的aiohttp.web.Request实例。在处理时aiohttp.web.Request.match_info会被设置为None。
      返回:
         返回AbstractMathcInfo实例对象。

class aiohttp.abc.AbstractMatchInfo
   抽象的匹配信息,由AbstractRouter.resolve()返回。
   http_exception
      如果没有匹配到信息则是aiohttp.web.HTTPException否则是None。
   coroutine handler(request)
      执行web处理器程序的抽象方法。
      参数:
         request - 用于处理请求的aiohttp.web.Request实例。在处理时aiohttp.web.Request.match_info会被设置为None。
      返回:
         返回aiohttp.web.StreamResponse或其派生类。
      可能会抛出的异常:
         所抛出的异常类型是aiohttp.web.HTTPException。
   coroutine expect_handler(request)
      用户处理100-continue的抽象方法。

创建视图

from aiohttp import web
async def index(request):return web.Response(text='Hello Aiohttp!')

视图函数可以放在类中,这些没有什么限制

class Handler:def __init__(self):passdef handle_intro(self, request):return web.Response(text="Hello, world")async def handle_greeting(self, request):name = request.match_info.get('name', "Anonymous")txt = "Hello, {}".format(name)return web.Response(text=txt)
handler = Handler()
app.router.add_get('/intro', handler.handle_intro)
app.router.add_get('/greet/{name}', handler.handle_greeting)

django风格的基础试图类。

class MyView(web.View):async def get(self):return await get_resp(self.request)async def post(self):return await post_resp(self.request)

处理器应是一个协程方法,且只接受self参数,并且返回标准web处理器的响应对象。请求对象可使用View.request中取出。

app.router.add_route('*', '/path/to', MyView)

抽象类基础视图

aiohttp提供抽象类 AbstractView来对基于类的视图进行支持,而且还是一个awaitable对象(可以应用在await Cls()或yield from Cls()中,同时还有一个request属性。)

class aiohttp.AbstarctView
    一个用于部署所有基于类的视图的基础类。
    __iter____await__方法需要被覆盖。
    request
      用于处理请求的aiohttp.web.Request实例。

请求(Request)和基础请求(BaseRequest)

Request 对象中包含所有的HTTP请求信息。
BaseRequest 用在底层服务器中(底层服务器没有应用,路由,信号和中间件)。Request对象拥有Request.app和Request.match_info属性。
BaseRequest和Reuqest都是类字典对象,以便在中间件和信号处理器中共享数据。

class aiohttp.web.BaseRequest

  • version
    发起请求的HTTP版本,该属性只读。
    返回aiohttp.protocol.HttpVersion实例对象。

  • method
    发起请求的HTTP方法,该属性只读。
    返回值为大写字符串,如”GET” ,”POST”,”PUT”。

  • url
    包含资源的绝对路径的URL实例对象。
    注意:如果是不合适的请求(如没有HOST HTTP头信息)则是不可用的。

  • rel_url
        包含资源的相对路径的URL实例对象。
        与.url.relative()相同。

  • scheme
      表示请求中的协议(scheme)部分。
           如果处理方式是SSL则为”https”,否则是”http”。
           该属性的值可能会被 clone()方法覆盖。
           该属性只读,类型为str。
           2.3版本时更改内容: Forwarded 和 X-Forwarded-Proto不在被使用。
           调用 .clone(scheme=new_scheme)来设置一个新的值。

参考:https://www.bookstack.cn/read/aiohttp-chinese-documentation/aiohttp%E6%96%87%E6%A1%A3-ServerReference.md

响应类

返回JSON 响应

def handler(request):data = {'some': 'data'}return web.json_response(data)

这个方法返回的是aiohttp.web.Response实例对象,所以你可以做些其他的事,比如设置cookies。

使用配置文件

# load config from yaml file in current dir
conf = load_config(str(pathlib.Path('.') / 'config' / 'polls.yaml'))
app['config'] = conf# 使用变量
conf = app['config']
name=conf['name'],

数据库架构

import sqlalchemy as sa
meta = sa.MetaData()
question - sq.Table('question', meta,sa.Column('id', sa.Integer, nullable=False),sa.Column('question_text', sa.String(200), nullable=False),sa.Column('pub_date', sa.Date, nullable=False),# Indexes #sa.PrimaryKeyConstraint('id', name='question_id_pkey')
)
choice = sa.Table('choice', meta,sa.Column('id', sa.Integer, nullable=False),sa.Column('question_id', sa.Integer, nullable=False),sa.Column('choice_text', sa.String(200), nullable=False),sa.Column('votes', server_default="0", nullable=False),# Indexes #sa.PrimayKeyConstraint('id', name='choice_id_pkey'),sa.ForeignKeyContraint(['question_id'], [question.c.id],name='choice_question_id_fkey',ondelete='CASCADE'),
)

创建连接引擎

async def init_pg(app):conf = app['config']engine = await aiopg.sa.create_engine(database=conf['database'],user=conf['user'],password=conf['password'],host=conf['host'],port=conf['host'],minsize=conf['minsize'],maxsize=conf['maxsize'])app['db'] = engine

最好将连接数据库的函数放在on_startup信号中:

app.on_startup.append(init_pg)

关闭数据库

程序退出时一块关闭所有的资源接口是一个很好的做法。
使用on_cleanup信号来关闭数据库接口:

async def close_pg(app):app['db'].close()await app['db'].wait_closed()
app.on_cleanup.append(close_pg)

使用模板

先安装

$ pip install aiohttp_jinja2

配置模板文件夹

我们将其放在polls/aiohttpdemo_polls/templates文件夹中。

import aiohttp_jinja2
import jinja2
aiohttp_jinja2.setup(app, loader=jinja2.PackageLoader('aiohttpdemo_polls', 'templates'))或者
app = web.Application()
aiohttp_jinja2.setup(app,loader=jinja2.FileSystemLoader('/path/to/templates/folder'))

视图函数

@aiohttp_jinja2.template('detail.html')
async def poll(request):async with request['db'].acquire() as conn:question_id = request.match_info['question_id']try:question, choices = await db.get_question(conn,question_id)except db.RecordNotFound as e:raise web.HTTPNotFound(text=str(e))return {'question': question,'choices': choices}

静态文件

app.router.add_static('/prefix/',path=str(project_root / 'static'),name='static')

当访问静态文件的目录时,默认服务器会返回 HTTP/403 Forbidden(禁止访问)。 使用show_index并将其设置为True可以显示出索引:

app.router.add_static('/prefix', path_to_static_folder, show_index=True)

当从静态文件目录访问一个符号链接(软链接)时,默认服务器会响应 HTTP/404 Not Found(未找到)。使用follow_symlinks并将其设置为True可以让服务器使用符号链接:

app.router.add_static('/prefix', path_to_static_folder, follow_symlinks=True)

如果你想允许缓存清除,使用append_version并设为True。

缓存清除会对资源文件像JavaScript 和 CSS文件等的文件名上添加一个hash后的版本。这样的好处是我们可以让浏览器无限期缓存这些文件而不用担心这些文件是否发布了新版本。

app.router.add_static('/prefix', path_to_static_folder, append_version=True)

使用中间件

中间件是每个web处理器必不可少的组件。它的作用是在处理器处理请求前预处理请求以及在得到响应后发送出去。相当于kong网关的插件。

我们下面来实现一个用于显示漂亮的404和500页面的简单中间件。

def setup_middlewares(app):error_middleware = error_pages({404: handle_404,500: handle_500})app.middlewares.append(error_middleware)

中间件(middleware)本身是一个接受应用程序(application)和后续处理器(next handler)的加工厂。

中间件工厂返回一个与web处理器一样接受请求并返回响应的中间件处理器。

def error_pages(overrides):async def middleware(app, handler):async def middleware_handler(request):try:response = await handler(request)override = overrides.get(response.status)if override is None:return responseelse:return await override(request, response)except web.HTTPException as ex:override = overrides.get(ex.status)if override is None:raiseelse:return await override(request, ex)return middleware_handlerreturn middleware

这些overrides(handle_404和handle_500)只是简单的用Jinja2模板渲染:

async def handle_404(request, response):response = aiohttp_jinja2.render_template('404.html',request,{})return response
async def handle_500(request, response):response = aiohttp_jinja2.render_template('500.html',request,{})return response

以下代码演示了中间件的执行顺序:

from aiohttp import webasync def test(request):print('Handler function called')return web.Response(text="Hello")@web.middleware
async def middleware1(request, handler):print('Middleware 1 called')response = await handler(request)print('Middleware 1 finished')return response@web.middleware
async def middleware2(request, handler):print('Middleware 2 called')response = await handler(request)print('Middleware 2 finished')return responseapp = web.Application(middlewares=[middleware1, middleware2])
app.router.add_get('/', test)
web.run_app(app)

输出内容为

Middleware 1 called
Middleware 2 called
Handler function called
Middleware 2 finished
Middleware 1 finished

aiohttp进阶教程相关推荐

  1. javascript进阶教程第一章案例实战

    javascript进阶教程第一章案例实战 一.学习任务 通过几个案例练习回顾学过的知识 通过练习积累JS的使用技巧 二.实例 练习1:删除确认提示框 实例描述: 防止用户小心单击了"删除& ...

  2. JSP WEB开发入门基础到高手进阶教程002

    JSP WEB开发入门基础到高手进阶教程 -------开发入门 JSP与微软的Active Server Pages 兼容,但它是使用类似HTML的卷标以及Java程序代码段而不是VBScript. ...

  3. duilib进阶教程 -- 在MFC中使用duilib (1)

    由于入门教程的反响还不错,因此Alberl就以直播的形式来写<进阶教程>啦,本教程的前提: 1.请先阅读<仿迅雷播放器教程> 2.要有一定的duilib基础,如果还没,请先阅读 ...

  4. 爬虫进阶教程:极验(GEETEST)验证码破解教程

    原文链接及原作者:爬虫进阶教程:极验(GEETEST)验证码破解教程 | Jack Cui 一.前言 爬虫最大的敌人之一是什么?没错,验证码![Geetest]作为提供验证码服务的行家,市场占有率还是 ...

  5. 《Web前端开发精品课 HTML与CSS进阶教程》——1.4 id和class

    本节书摘来自异步社区<Web前端开发精品课 HTML与CSS进阶教程>一书中的第1章,第1.4节,作者: 莫振杰 更多章节内容可以访问云栖社区"异步社区"公众号查看. ...

  6. STM32 进阶教程 20 - 串口+DMA实现OneWire总线

    前言 One-wire总线使用一根并联总线完成对于多个设备的访问,通过上拉的OD门实现多设备的读写操作,通过ID区别设备,通过CRC5完成数据校验.常见对于one-wire总线的操作代码主要使用包含基 ...

  7. STM32 进阶教程 19 - IQmath数学库的使用

    前言 STM32 M3 系列是不带浮点运算单元的,小数运算都是采用定点转浮点试式实现的,本节给大家介绍一个很好用的定点转浮点数学运算库,IQmath是德州仪器 (TI) 的一个高度优化的高精度数学函数 ...

  8. STM32 进阶教程 18 – ADC间断模式

    前言 STM32 的ADC拥有连续扫描模式,也有间断模式,间断模式较扫描模式需要更多的触发事件才能完成所有的通道转换操作,在实际工程应用中,可以利用间断模式实现一些特殊应用.关于间断模式,在STM32 ...

  9. STM32 进阶教程 17 - ADC注入通道

    前言 STM32 的ADC的一个强大功能是支持触发注入功能,在103中每个ADC模块支持4个注入通道,每个注入通道具有独立的结果突存器,注入通道具有较规划通道更高的优先级,在实际工程应用中,注入通道更 ...

最新文章

  1. 数据中心大火波及360万网站,或因UPS故障,3月10日晚法国斯特拉斯堡
  2. mysql5.7数据恢复_mysql 5.7.21 解压版通过历史data目录恢复数据的教程图解
  3. KeyUsage Extension The KeyUsage extension defines the following variables, which correlate directly
  4. matlab判断文件是否损坏,检查 MATLAB 代码文件是否有问题
  5. Spring Cloud微服务之子模块的创建(二)
  6. oracle是delete可以加并行吗,提高Oracle DELETE性能的策略
  7. 前端ajax数据提交到服务器_详解前端如何让服务器主动向浏览器推送数据
  8. 【计算机网络】许多信道不能传输低频、直流分量的原因
  9. atheros无线网卡驱动_5.8G无线网桥CPE,安防监控拍档高清无干扰
  10. ubuntu18.04 ssh 远程系统拒绝连接 解决方法
  11. MFC中的几个虚函数
  12. php查netstat,Netstat命令详解
  13. php网站建设步骤,「php环境搭建」简单6个步骤教会你快速搭建一个网站(windows环境) - seo实验室...
  14. LeCo-83.删除排序链表中的重复元素
  15. H3C无线控制器AP license共享配置
  16. 餐饮店如何做活动吸引人
  17. Android学习资源汇总
  18. 2010年安防企业与事件盘点
  19. BIN文件转dfu文件 进行刷机 问题
  20. Teamviewer - Teamviewer被检测成商用,无法使用个人版怎么解决(不用修改Mac地址)

热门文章

  1. GMM-HMM语音识别原理详解 - 全文
  2. 语音识别kaldi该如何学习?
  3. 万能倍投计算器工具_一周总结上证A股市盈率14.83倍,这是机会还是风险呢?
  4. vue elementui表格数据
  5. Vue打包并发布项目
  6. idea工具修改Git路径
  7. 【PAT】2021年冬季考试甲级,摸鱼游记、92分
  8. 【PAT乙】1004 成绩排名 (20分) struct结构
  9. android 高德地图动画,点动态样式-基本功能-示例中心-Loca API 示例 | 高德地图API...
  10. osg加载osgb数据_铁路工程三维协同大数据云平台研究与开发