mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-06 07:50:32 +00:00
Update middlewares docs
This commit is contained in:
parent
7f26ec9935
commit
bafc2ff341
11 changed files with 119 additions and 215 deletions
|
|
@ -5,6 +5,7 @@ from .api.client import session
|
|||
from .api.client.bot import Bot
|
||||
from .dispatcher import filters, handler
|
||||
from .dispatcher.dispatcher import Dispatcher
|
||||
from .dispatcher.middlewares.base import BaseMiddleware
|
||||
from .dispatcher.router import Router
|
||||
|
||||
try:
|
||||
|
|
@ -24,6 +25,7 @@ __all__ = (
|
|||
"session",
|
||||
"Dispatcher",
|
||||
"Router",
|
||||
"BaseMiddleware",
|
||||
"filters",
|
||||
"handler",
|
||||
)
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 51 KiB |
|
|
@ -39,7 +39,7 @@ dp.include_router(router1)
|
|||
## Handling updates
|
||||
All updates can be propagated to the dispatcher by `feed_update` method:
|
||||
|
||||
```
|
||||
```python3
|
||||
bot = Bot(...)
|
||||
dp = Dispathcher()
|
||||
|
||||
|
|
|
|||
95
docs/dispatcher/middlewares.md
Normal file
95
docs/dispatcher/middlewares.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Middlewares
|
||||
|
||||
**aiogram** provides powerful mechanism for customizing event handlers via middlewares.
|
||||
|
||||
Middlewares in bot framework seems like Middlewares mechanism in 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 two layers of middlewares (before and after filters).
|
||||
|
||||
!!! 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 in many places of pipeline.
|
||||
|
||||
## Basics
|
||||
|
||||
Middleware instance can be applied for every type of Telegram Event (Update, Message, etc.) in two places
|
||||
|
||||
1. Outer scope - before processing filters (`#!python <router>.<event>.outer_middleware(...)`)
|
||||
2. Inner scope - after processing filters but before handler (`#!python <router>.<event>.middleware(...)`)
|
||||
|
||||
[](../assets/images/basics_middleware.png)
|
||||
|
||||
_(Click on image to zoom it)_
|
||||
|
||||
!!! warning
|
||||
|
||||
Middleware should be subclass of `BaseMiddleware` (`#!python3 from aiogram import BaseMiddleware`) or any async callable
|
||||
|
||||
## Arguments specification
|
||||
| Argument | Type | Description |
|
||||
| - | - | - |
|
||||
| `handler` | `#!python Callable[[T, Dict[str, Any]], Awaitable[Any]]` | Wrapped handler in middlewares chain |
|
||||
| `event` | `#!python T` | Incoming event (Subclass of `TelegramObject`) |
|
||||
| `data` | `#!python Dict[str, Any]` | Contextual data. Will be mapped to handler arguments |
|
||||
|
||||
## Examples
|
||||
|
||||
!!! danger
|
||||
|
||||
Middleware should always call `#!python await handler(event, data)` to propagate event for next middleware/handler
|
||||
|
||||
### Class-based
|
||||
```python3
|
||||
from aiogram import BaseMiddleware
|
||||
from aiogram.api.types import Message
|
||||
|
||||
|
||||
class CounterMiddleware(BaseMiddleware[Message]):
|
||||
def __init__(self) -> None:
|
||||
self.counter = 0
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
handler: Callable[[Message, Dict[str, Any]], Awaitable[Any]],
|
||||
event: Message,
|
||||
data: Dict[str, Any]
|
||||
) -> Any:
|
||||
self.counter += 1
|
||||
data['counter'] = self.counter
|
||||
return await handler(event, data)
|
||||
```
|
||||
and then
|
||||
```python3
|
||||
router = Router()
|
||||
router.message.middleware(CounterMiddleware())
|
||||
```
|
||||
|
||||
### Function-based
|
||||
```python3
|
||||
@dispatcher.update.outer_middleware()
|
||||
async def database_transaction_middleware(
|
||||
handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
|
||||
event: Update,
|
||||
data: Dict[str, Any]
|
||||
) -> Any:
|
||||
async with database.transaction():
|
||||
return await handler(event, data)
|
||||
```
|
||||
|
||||
## Facts
|
||||
|
||||
1. Middlewares from outer scope will be called on every incoming event
|
||||
1. Middlewares from inner scope will be called only when filters pass
|
||||
1. Inner middlewares is always calls for `Update` event type in due to all incoming updates going to specific event type handler through built in update handler
|
||||
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
# 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`
|
||||
- `#!python3 error`
|
||||
|
||||
## 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
|
||||
- `#!python3 on_pre_process_error` - will be triggered on **pre process** `#!python3 error` event
|
||||
- `#!python3 on_process_error` - will be triggered on **process** `#!python3 error` event
|
||||
- `#!python3 on_post_process_error` - will be triggered on **post process** `#!python3 error` event
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
# Overview
|
||||
|
||||
**aiogram** provides powerful mechanism for customizing event handlers via middlewares.
|
||||
|
||||
Middlewares in bot framework seems like Middlewares mechanism in 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 two 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.
|
||||
|
||||
[](../../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)
|
||||
|
||||
!!! warning
|
||||
When filters does not match any handler with this event the `#!python3 process`
|
||||
step will not be called.
|
||||
|
||||
!!! warning
|
||||
When exception will be caused in handlers pipeline will be stopped immediately
|
||||
and then start processing error via errors handler and it own middleware callbacks.
|
||||
|
||||
!!! warning
|
||||
Middlewares for updates will be called for all routers in tree but callbacks for events
|
||||
will be called only for specific branch of routers.
|
||||
|
||||
### Pipeline in pictures:
|
||||
|
||||
#### Simple pipeline
|
||||
|
||||
[](../../assets/images/middleware_pipeline.png)
|
||||
|
||||
_(Click on image to zoom it)_
|
||||
|
||||
#### Nested routers pipeline
|
||||
|
||||
[](../../assets/images/middleware_pipeline_nested.png)
|
||||
|
||||
_(Click on image to zoom it)_
|
||||
|
||||
## Read more
|
||||
|
||||
- [Middleware Basics](basics.md)
|
||||
|
|
@ -20,7 +20,7 @@ Documentation for version 3.0 [WIP] [^1]
|
|||
- [Supports Telegram Bot API v{!_api_version.md!}](api/index.md)
|
||||
- [Updates router](dispatcher/index.md) (Blueprints)
|
||||
- Finite State Machine
|
||||
- [Middlewares](dispatcher/middlewares/index.md)
|
||||
- [Middlewares](dispatcher/middlewares.md)
|
||||
- [Replies into Webhook](https://core.telegram.org/bots/faq#how-can-i-make-requests-in-response-to-updates)
|
||||
|
||||
|
||||
|
|
|
|||
12
docs/stylesheets/extra.css
Normal file
12
docs/stylesheets/extra.css
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
@font-face {
|
||||
font-family: 'JetBrainsMono';
|
||||
src: url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2') format('woff2'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Regular.woff') format('woff'),
|
||||
url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/ttf/JetBrainsMono-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
code, kbd, pre {
|
||||
font-family: "JetBrainsMono", "Roboto Mono", "Courier New", Courier, monospace;
|
||||
}
|
||||
|
|
@ -16,6 +16,9 @@ theme:
|
|||
favicon: 'assets/images/logo.png'
|
||||
logo: 'assets/images/logo.png'
|
||||
|
||||
extra_css:
|
||||
- stylesheets/extra.css
|
||||
|
||||
extra:
|
||||
version: 3.0.0a3
|
||||
|
||||
|
|
@ -255,9 +258,8 @@ nav:
|
|||
- dispatcher/class_based_handlers/pre_checkout_query.md
|
||||
- dispatcher/class_based_handlers/shipping_query.md
|
||||
- dispatcher/class_based_handlers/error.md
|
||||
- Middlewares:
|
||||
- dispatcher/middlewares/index.md
|
||||
- dispatcher/middlewares/basics.md
|
||||
- dispatcher/middlewares.md
|
||||
|
||||
- todo.md
|
||||
- Build reports:
|
||||
- reports.md
|
||||
|
|
|
|||
16
poetry.lock
generated
16
poetry.lock
generated
|
|
@ -434,14 +434,6 @@ html5 = ["html5lib"]
|
|||
htmlsoup = ["beautifulsoup4"]
|
||||
source = ["Cython (>=0.29.7)"]
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "This package provides magic filter based on dynamic attribute getter"
|
||||
name = "magic-filter"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1,<4.0.0"
|
||||
version = "0.1.2"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python implementation of Markdown."
|
||||
|
|
@ -963,7 +955,7 @@ python-versions = "*"
|
|||
version = "1.4.1"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
category = "dev"
|
||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||
name = "typing-extensions"
|
||||
optional = false
|
||||
|
|
@ -1039,7 +1031,7 @@ fast = ["uvloop"]
|
|||
proxy = ["aiohttp-socks"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "5c53527f09e65af097aa3d3a25e41646e8b8a0dda25e96445ceef969c19297e5"
|
||||
content-hash = "768759359beca8b84811bfc21adac9649925cd22b87427a10608c9d1e16a0923"
|
||||
python-versions = "^3.7"
|
||||
|
||||
[metadata.files]
|
||||
|
|
@ -1256,10 +1248,6 @@ lxml = [
|
|||
{file = "lxml-4.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5b3c4b7edd2e770375a01139be11307f04341ec709cf724e0f26ebb1eef12c3"},
|
||||
{file = "lxml-4.5.0.tar.gz", hash = "sha256:8620ce80f50d023d414183bf90cc2576c2837b88e00bea3f33ad2630133bbb60"},
|
||||
]
|
||||
magic-filter = [
|
||||
{file = "magic-filter-0.1.2.tar.gz", hash = "sha256:dfd1a778493083ac1355791d1716c3beb6629598739f2c2ec078815952282c1d"},
|
||||
{file = "magic_filter-0.1.2-py3-none-any.whl", hash = "sha256:16d0c96584f0660fd7fa94b6cd16f92383616208a32568bf8f95a57fc1a69e9d"},
|
||||
]
|
||||
markdown = [
|
||||
{file = "Markdown-3.2.1-py2.py3-none-any.whl", hash = "sha256:e4795399163109457d4c5af2183fbe6b60326c17cfdf25ce6e7474c6624f725d"},
|
||||
{file = "Markdown-3.2.1.tar.gz", hash = "sha256:90fee683eeabe1a92e149f7ba74e5ccdc81cd397bd6c516d93a8da0ef90b6902"},
|
||||
|
|
|
|||
|
|
@ -40,8 +40,6 @@ aiofiles = "^0.4.0"
|
|||
uvloop = {version = "^0.14.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'", optional = true}
|
||||
async_lru = "^1.0"
|
||||
aiohttp-socks = {version = "^0.3.8", optional = true}
|
||||
typing-extensions = "^3.7.4"
|
||||
magic-filter = "^0.1.2"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
uvloop = {version = "^0.14.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'"}
|
||||
|
|
@ -69,6 +67,7 @@ markdown-include = "^0.5.1"
|
|||
aiohttp-socks = "^0.3.4"
|
||||
pre-commit = "^2.3.0"
|
||||
packaging = "^20.3"
|
||||
typing-extensions = "^3.7.4"
|
||||
|
||||
[tool.poetry.extras]
|
||||
fast = ["uvloop"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue