在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 web async 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 interruption await 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() 。
抽象路由类
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): pass def 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)
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)
关闭数据库
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 response else: return await override(request, response) except web.HTTPException as ex: override = overrides.get(ex.status) if override is None: raise else: return await override(request, ex) return middleware_handler return 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 web async 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 response app = 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
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214360.html原文链接:https://javaforall.net
