@@ -226,6 +226,10 @@ async def handle(
226226 ``/user/{user_id:int}/``), NOT against the actual **request path** (e.g.,
227227 ``/user/1234/``). This is a critical distinction for dynamic routes.
228228
229+ If you need to exclude based on paths dynamically, use
230+ :attr:`~litestar.middleware.ASGIMiddleware.should_bypass_for_scope`
231+ instead, matching on ``scope["path"]``.
232+
229233 **Example 1: Static path**
230234
231235 Handler path::
@@ -250,19 +254,44 @@ async def handle(
250254 /user/5678/profile
251255 /user/9999/profile
252256
253- To exclude this handler, the pattern must match the **handler**, not the actual
254- request path:
257+ To exclude this handler, the pattern must match the **handler**, not the actual request path::
255258
256259 exclude_path_pattern = "/user/{user_id:int}/profile"
257260 exclude_path_pattern = "/user/\{.+?\}/"
258261 """
259262 exclude_opt_key : str | None = None
263+ """
264+ Exclude this middleware for handlers with an opt-key of this name that is truthy
265+ """
266+ should_bypass_for_scope : Callable [[Scope ], bool ] | None = None
267+ r"""
268+ A callable that takes in the :class:`~litestar.types.Scope` of the current
269+ connection and returns a boolean, indicating if the middleware should be skipped for
270+ the current request.
271+
272+ This can for example be used to exclude a middleware based on a dynamic path::
273+
274+ should_bypass_for_scope = lambda scope: scope["path"].endswith(".jpg")
275+
276+ Applied to a route with a dynamic path like ``/static/{file_name:str}``, it would
277+ be skipped *only* if ``file_name`` has a ``.jpg`` extension.
278+
279+ .. note::
280+
281+ If it is not required to dynamically match the path of a request,
282+ :attr:`~litestar.middleware.ASGIMiddleware.exclude_path_pattern` should be
283+ used instead. Since its exclusion is done statically at startup time, it has no
284+ performance cost at runtime.
285+
286+ .. versionadded:: 3.0
287+ """
260288 constraints : MiddlewareConstraints | None = None
261289
262290 def should_bypass_for_handler (self , handler : RouteHandlerType ) -> bool :
263291 """Return ``True`` if this middleware should be bypassed for ``handler``, according
264- to ``scopes``, ``exclude_path_pattern`` or ``exclude_opt_key``, otherwise
265- ``False``.
292+ to :attr:`~litestar.middleware.ASGIMiddleware.scopes`,
293+ :attr:`~litestar.middleware.ASGIMiddleware.exclude_path_pattern` or
294+ :attr:`~litestar.middleware.ASGIMiddleware.exclude_opt_key`, otherwise ``False``.
266295 """
267296 from litestar .handlers import ASGIRouteHandler , HTTPRouteHandler , WebsocketRouteHandler
268297
@@ -286,8 +315,18 @@ def __call__(self, app: ASGIApp) -> ASGIApp:
286315 """Create the actual middleware callable"""
287316 handle = self .handle
288317
289- async def middleware (scope : Scope , receive : Receive , send : Send ) -> None :
290- await handle (scope = scope , receive = receive , send = send , next_app = app )
318+ should_bypass_for_scope = self .should_bypass_for_scope
319+ if should_bypass_for_scope is None :
320+
321+ async def middleware (scope : Scope , receive : Receive , send : Send ) -> None :
322+ await handle (scope = scope , receive = receive , send = send , next_app = app )
323+ else :
324+
325+ async def middleware (scope : Scope , receive : Receive , send : Send ) -> None :
326+ if should_bypass_for_scope (scope ):
327+ await app (scope , receive , send )
328+ else :
329+ await handle (scope = scope , receive = receive , send = send , next_app = app )
291330
292331 return middleware
293332
0 commit comments