@@ -568,9 +568,13 @@ A *middleware* is a coroutine that can modify either the request or
568
568
response. For example, here's a simple *middleware * which appends
569
569
``' wink' `` to the response::
570
570
571
- from aiohttp.web import middleware
571
+ from aiohttp import web
572
+ from typing import Callable, Awaitable
572
573
573
- async def middleware(request, handler):
574
+ async def middleware(
575
+ request: web.Request,
576
+ handler: Callable[[web.Request], Awaitable[web.StreamResponse]]
577
+ ) -> web.StreamResponse:
574
578
resp = await handler(request)
575
579
resp.text = resp.text + ' wink'
576
580
return resp
@@ -619,18 +623,25 @@ post-processing like handling *CORS* and so on.
619
623
The following code demonstrates middlewares execution order::
620
624
621
625
from aiohttp import web
626
+ from typing import Callable, Awaitable
622
627
623
- async def test(request) :
628
+ async def test(request: web.Request) -> web.Response :
624
629
print('Handler function called')
625
630
return web.Response(text="Hello")
626
631
627
- async def middleware1(request, handler):
632
+ async def middleware1(
633
+ request: web.Request,
634
+ handler: Callable[[web.Request], Awaitable[web.StreamResponse]]
635
+ ) -> web.StreamResponse:
628
636
print('Middleware 1 called')
629
637
response = await handler(request)
630
638
print('Middleware 1 finished')
631
639
return response
632
640
633
- async def middleware2(request, handler):
641
+ async def middleware2(
642
+ request: web.Request,
643
+ handler: Callable[[web.Request], Awaitable[web.StreamResponse]]
644
+ ) -> web.StreamResponse:
634
645
print('Middleware 2 called')
635
646
response = await handler(request)
636
647
print('Middleware 2 finished')
@@ -649,6 +660,82 @@ Produced output::
649
660
Middleware 2 finished
650
661
Middleware 1 finished
651
662
663
+ Request Body Stream Consumption
664
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
665
+
666
+ .. warning ::
667
+
668
+ When middleware reads the request body (using :meth: `~aiohttp.web.BaseRequest.read `,
669
+ :meth: `~aiohttp.web.BaseRequest.text `, :meth: `~aiohttp.web.BaseRequest.json `, or
670
+ :meth: `~aiohttp.web.BaseRequest.post `), the body stream is consumed. However, these
671
+ high-level methods cache their result, so subsequent calls from the handler or other
672
+ middleware will return the same cached value.
673
+
674
+ The important distinction is:
675
+
676
+ - High-level methods (:meth: `~aiohttp.web.BaseRequest.read `, :meth: `~aiohttp.web.BaseRequest.text `,
677
+ :meth: `~aiohttp.web.BaseRequest.json `, :meth: `~aiohttp.web.BaseRequest.post `) cache their
678
+ results internally, so they can be called multiple times and will return the same value.
679
+ - Direct stream access via :attr: `~aiohttp.web.BaseRequest.content ` does NOT have this
680
+ caching behavior. Once you read from ``request.content `` directly (e.g., using
681
+ ``await request.content.read() ``), subsequent reads will return empty bytes.
682
+
683
+ Consider this middleware that logs request bodies::
684
+
685
+ from aiohttp import web
686
+ from typing import Callable, Awaitable
687
+
688
+ async def logging_middleware(
689
+ request: web.Request,
690
+ handler: Callable[[web.Request], Awaitable[web.StreamResponse]]
691
+ ) -> web.StreamResponse:
692
+ # This consumes the request body stream
693
+ body = await request.text()
694
+ print(f"Request body: {body}")
695
+ return await handler(request)
696
+
697
+ async def handler(request: web.Request) -> web.Response:
698
+ # This will return the same value that was read in the middleware
699
+ # (i.e., the cached result, not an empty string)
700
+ body = await request.text()
701
+ return web.Response(text=f"Received: {body}")
702
+
703
+ In contrast, when accessing the stream directly (not recommended in middleware)::
704
+
705
+ async def stream_middleware(
706
+ request: web.Request,
707
+ handler: Callable[[web.Request], Awaitable[web.StreamResponse]]
708
+ ) -> web.StreamResponse:
709
+ # Reading directly from the stream - this consumes it!
710
+ data = await request.content.read()
711
+ print(f"Stream data: {data}")
712
+ return await handler(request)
713
+
714
+ async def handler(request: web.Request) -> web.Response:
715
+ # This will return empty bytes because the stream was already consumed
716
+ data = await request.content.read()
717
+ # data will be b'' (empty bytes)
718
+
719
+ # However, high-level methods would still work if called for the first time:
720
+ # body = await request.text() # This would read from internal cache if available
721
+ return web.Response(text=f"Received: {data}")
722
+
723
+ When working with raw stream data that needs to be shared between middleware and handlers::
724
+
725
+ async def stream_parsing_middleware(
726
+ request: web.Request,
727
+ handler: Callable[[web.Request], Awaitable[web.StreamResponse]]
728
+ ) -> web.StreamResponse:
729
+ # Read stream once and store the data
730
+ raw_data = await request.content.read()
731
+ request['raw_body'] = raw_data
732
+ return await handler(request)
733
+
734
+ async def handler(request: web.Request) -> web.Response:
735
+ # Access the stored data instead of reading the stream again
736
+ raw_data = request.get('raw_body', b'')
737
+ return web.Response(body=raw_data)
738
+
652
739
Example
653
740
^^^^^^^
654
741
0 commit comments