Add middlewares (API + Docs + Tests)

This commit is contained in:
Alex Root Junior 2020-04-12 20:27:32 +03:00
parent e4cd4c1763
commit 5b6ec599b1
24 changed files with 1120 additions and 42 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -0,0 +1,111 @@
# Basics
All middlewares should be made with `BaseMiddleware` (`#!python3 from aiogram import BaseMiddleware`) as base class.
For example:
```python3
class MyMiddleware(BaseMiddleware): ...
```
And then use next pattern in naming callback functions in middleware: `on_{step}_{event}`
Where is:
- `#!python3 step`:
- `#!python3 pre_process`
- `#!python3 process`
- `#!python3 post_process`
- `#!python3 event`:
- `#!python3 update`
- `#!python3 message`
- `#!python3 edited_message`
- `#!python3 channel_post`
- `#!python3 edited_channel_post`
- `#!python3 inline_query`
- `#!python3 chosen_inline_result`
- `#!python3 callback_query`
- `#!python3 shipping_query`
- `#!python3 pre_checkout_query`
- `#!python3 poll`
- `#!python3 poll_answer`
## Connecting middleware with router
Middlewares can be connected with router by next ways:
1. `#!python3 router.use(MyMiddleware())` (**recommended**)
1. `#!python3 router.middleware.setup(MyMiddleware())`
1. `#!python3 MyMiddleware().setup(router.middleware)` (**not recommended**)
!!! warning
One instance of middleware **can't** be registered twice in single or many middleware managers
## The specification of step callbacks
### Pre-process step
| Argument | Type | Description |
| --- | --- | --- |
| event name | Any of event type (Update, Message and etc.) | Event |
| `#!python3 data` | `#!python3 Dict[str, Any]` | Contextual data (Will be mapped to handler arguments) |
Returns `#!python3 Any`
### Process step
| Argument | Type | Description |
| --- | --- | --- |
| event name | Any of event type (Update, Message and etc.) | Event |
| `#!python3 data` | `#!python3 Dict[str, Any]` | Contextual data (Will be mapped to handler arguments) |
Returns `#!python3 Any`
### Post-Process step
| Argument | Type | Description |
| --- | --- | --- |
| event name | Any of event type (Update, Message and etc.) | Event |
| `#!python3 data` | `#!python3 Dict[str, Any]` | Contextual data (Will be mapped to handler arguments) |
| `#!python3 result` | `#!python3 Dict[str, Any]` | Response from handlers |
Returns `#!python3 Any`
## Full list of available callbacks
- `#!python3 on_pre_process_update` - will be triggered on **pre process** `#!python3 update` event
- `#!python3 on_process_update` - will be triggered on **process** `#!python3 update` event
- `#!python3 on_post_process_update` - will be triggered on **post process** `#!python3 update` event
- `#!python3 on_pre_process_message` - will be triggered on **pre process** `#!python3 message` event
- `#!python3 on_process_message` - will be triggered on **process** `#!python3 message` event
- `#!python3 on_post_process_message` - will be triggered on **post process** `#!python3 message` event
- `#!python3 on_pre_process_edited_message` - will be triggered on **pre process** `#!python3 edited_message` event
- `#!python3 on_process_edited_message` - will be triggered on **process** `#!python3 edited_message` event
- `#!python3 on_post_process_edited_message` - will be triggered on **post process** `#!python3 edited_message` event
- `#!python3 on_pre_process_channel_post` - will be triggered on **pre process** `#!python3 channel_post` event
- `#!python3 on_process_channel_post` - will be triggered on **process** `#!python3 channel_post` event
- `#!python3 on_post_process_channel_post` - will be triggered on **post process** `#!python3 channel_post` event
- `#!python3 on_pre_process_edited_channel_post` - will be triggered on **pre process** `#!python3 edited_channel_post` event
- `#!python3 on_process_edited_channel_post` - will be triggered on **process** `#!python3 edited_channel_post` event
- `#!python3 on_post_process_edited_channel_post` - will be triggered on **post process** `#!python3 edited_channel_post` event
- `#!python3 on_pre_process_inline_query` - will be triggered on **pre process** `#!python3 inline_query` event
- `#!python3 on_process_inline_query` - will be triggered on **process** `#!python3 inline_query` event
- `#!python3 on_post_process_inline_query` - will be triggered on **post process** `#!python3 inline_query` event
- `#!python3 on_pre_process_chosen_inline_result` - will be triggered on **pre process** `#!python3 chosen_inline_result` event
- `#!python3 on_process_chosen_inline_result` - will be triggered on **process** `#!python3 chosen_inline_result` event
- `#!python3 on_post_process_chosen_inline_result` - will be triggered on **post process** `#!python3 chosen_inline_result` event
- `#!python3 on_pre_process_callback_query` - will be triggered on **pre process** `#!python3 callback_query` event
- `#!python3 on_process_callback_query` - will be triggered on **process** `#!python3 callback_query` event
- `#!python3 on_post_process_callback_query` - will be triggered on **post process** `#!python3 callback_query` event
- `#!python3 on_pre_process_shipping_query` - will be triggered on **pre process** `#!python3 shipping_query` event
- `#!python3 on_process_shipping_query` - will be triggered on **process** `#!python3 shipping_query` event
- `#!python3 on_post_process_shipping_query` - will be triggered on **post process** `#!python3 shipping_query` event
- `#!python3 on_pre_process_pre_checkout_query` - will be triggered on **pre process** `#!python3 pre_checkout_query` event
- `#!python3 on_process_pre_checkout_query` - will be triggered on **process** `#!python3 pre_checkout_query` event
- `#!python3 on_post_process_pre_checkout_query` - will be triggered on **post process** `#!python3 pre_checkout_query` event
- `#!python3 on_pre_process_poll` - will be triggered on **pre process** `#!python3 poll` event
- `#!python3 on_process_poll` - will be triggered on **process** `#!python3 poll` event
- `#!python3 on_post_process_poll` - will be triggered on **post process** `#!python3 poll` event
- `#!python3 on_pre_process_poll_answer` - will be triggered on **pre process** `#!python3 poll_answer` event
- `#!python3 on_process_poll_answer` - will be triggered on **process** `#!python3 poll_answer` event
- `#!python3 on_post_process_poll_answer` - will be triggered on **post process** `#!python3 poll_answer` event

View file

@ -0,0 +1,65 @@
# Overview
**aiogram**'s provides powerful mechanism for customizing event handlers via middlewares.
Middlewares in bot framework seems like Middlewares mechanism in powerful web-frameworks
(like [aiohttp](https://docs.aiohttp.org/en/stable/web_advanced.html#aiohttp-web-middlewares),
[fastapi](https://fastapi.tiangolo.com/tutorial/middleware/),
[Django](https://docs.djangoproject.com/en/3.0/topics/http/middleware/) or etc.)
with small difference - here is implemented many layers of processing
(named as [pipeline](#event-pipeline)).
!!! info
Middleware is function that triggered on every event received from
Telegram Bot API in many points on processing pipeline.
## Base theory
As many books and other literature in internet says:
> Middleware is reusable software that leverages patterns and frameworks to bridge
>the gap between the functional requirements of applications and the underlying operating systems,
> network protocol stacks, and databases.
Middleware can modify, extend or reject processing event before-,
on- or after- processing of that event.
[![middlewares](../../assets/images/basics_middleware.png)](../../assets/images/basics_middleware.png)
_(Click on image to zoom it)_
## Event pipeline
As described below middleware an interact with event in many stages of pipeline.
Simple workflow:
1. Dispatcher receive an [Update](../../api/types/update.md)
1. Call **pre-process** update middleware in all routers tree
1. Filter Update over handlers
1. Call **process** update middleware in all routers tree
1. Router detects event type (Message, Callback query, etc.)
1. Router triggers **pre-process** <event> middleware of specific type
1. Pass event over [filters](../filters/index.md) to detect specific handler
1. Call **process** <event> middleware for specific type (only when handler for this event exists)
1. *Do magick*. Call handler (Read more [Event observers](../router.md#event-observers))
1. Call **post-process** <event> middleware
1. Call **post-process** update middleware in all routers tree
1. Emit response into webhook (when it needed)
### Pipeline in pictures:
#### Simple pipeline
[![middlewares](../../assets/images/middleware_pipeline.png)](../../assets/images/middleware_pipeline.png)
_(Click on image to zoom it)_
#### Nested routers pipeline
[![middlewares](../../assets/images/middleware_pipeline_nested.png)](../../assets/images/middleware_pipeline_nested.png)
_(Click on image to zoom it)_
## Read more
- [Middleware Basics](basics.md)

View file

@ -15,19 +15,19 @@ Documentation for version 3.0 [WIP] [^1]
## Features
- Asynchronous
- Asynchronous ([asyncio docs](https://docs.python.org/3/library/asyncio.html), [PEP-492](https://www.python.org/dev/peps/pep-0492/))
- [Supports Telegram Bot API v{!_api_version.md!}](api/index.md)
- [Updates router](dispatcher/index.md) (Blueprints)
- Finite State Machine
- Middlewares
- [Middlewares](dispatcher/middlewares/index.md)
- [Replies into Webhook](https://core.telegram.org/bots/faq#how-can-i-make-requests-in-response-to-updates)
!!! note
Before start using **aiogram** is highly recommend to know how to work with [asyncio](https://docs.python.org/3/library/asyncio.html).
Also if you has questions you can go to our community chats in Telegram:
- [English language](https://t.me/aiogram)
- [Russian language](https://t.me/aiogram_ru)

View file

@ -23,8 +23,8 @@
- [x] ContentTypes
- [x] Text
- [ ] ...
- [ ] Middlewares
- [ ] Engine
- [x] Middlewares
- [x] Engine
- [ ] Builtin middlewares
- [ ] ...
- [ ] Webhook
@ -41,6 +41,7 @@
- [x] Dispatcher
- [x] Router
- [x] Observers
- [x] Middleware
- [ ] Filters
- [ ] Utils
- [x] Helper