Skip to content

Conversation

@RobertoPrevato
Copy link
Member

No description provided.

…iddlewares

This commit implements a wrapper that enables BlackSheep applications to use
standard ASGI middlewares (like SentryAsgiMiddleware, OpenTelemetry middlewares,
etc.) by intercepting at the ASGI protocol level.

**Implementation:**
- ASGIMiddlewareWrapper: Class that wraps BlackSheep apps to support ASGI middlewares
- use_asgi_middleware(): Helper function for easy middleware integration
- Operates at ASGI protocol level (scope/receive/send) before BlackSheep's Request/Response abstraction

**Usage Example:**
```python
from blacksheep import Application
from blacksheep.middlewares import use_asgi_middleware
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware

app = Application()
# ... configure routes ...
app = use_asgi_middleware(app, SentryAsgiMiddleware)
```

**Benefits:**
- Maintains full backward compatibility with existing BlackSheep middlewares
- Allows integration with the ASGI ecosystem
- Multiple ASGI middlewares can be chained naturally
- Clean separation between ASGI and BlackSheep middleware layers

Fixes #263
…e placement

This commit enhances the ASGI middleware integration with a second approach
that allows ASGI middlewares to be inserted ANYWHERE in the BlackSheep
middleware chain, not just at the beginning.

**New Features:**

1. **ASGIContext Class**
   - Preserves ASGI scope/receive/send on Request objects
   - Caches request body for re-reading if needed
   - Enables ASGI middleware invocation at any middleware chain position

2. **enable_asgi_context() Function**
   - Wraps application to enable ASGI context preservation
   - Opt-in: only used when fine-grained control is needed
   - Minimal overhead when not enabled

3. **asgi_middleware_adapter() Function**
   - Converts ASGI middleware to BlackSheep middleware format
   - Handles Request/Response ↔ scope/receive/send conversions
   - Allows true interoperability between both middleware systems

**Two Approaches Now Available:**

**Approach 1: Simple Wrapper** (use_asgi_middleware)
- ASGI middlewares wrap the entire application
- Simple, zero overhead
- Best for: Sentry, outer-layer error tracking

**Approach 2: Context Preservation** (asgi_middleware_adapter)
- ASGI middlewares can be inserted anywhere in the chain
- Full flexibility to mix BlackSheep and ASGI middlewares
- Best for: Fine-grained control, complex middleware ordering

**Usage Examples:**

```python
# Approach 1: Simple wrapper
app = Application()
app = use_asgi_middleware(app, SentryAsgiMiddleware)

# Approach 2: Context preservation (anywhere in chain)
app = Application()
app = enable_asgi_context(app)
app.middlewares.append(blacksheep_middleware)
app.middlewares.append(asgi_middleware_adapter(SentryAsgiMiddleware))
app.middlewares.append(another_blacksheep_middleware)
```

**Testing:**
- Added comprehensive tests for both approaches
- Tests for mixed middleware scenarios
- Tests for error handling and edge cases

**Documentation:**
- Updated example to show both approaches
- Extensive docstrings with usage examples
- Clear guidance on when to use each approach

Related to #263
- Test that ASGI middleware can block request before handler executes
- Critical for authentication and authorization middlewares
- Verify both approaches correctly prevent handler execution when middleware responds early (e.g., 401 Unauthorized)
- All 16 tests passing
@RobertoPrevato
Copy link
Member Author

RobertoPrevato commented Feb 8, 2026

Not good. Claude Sonnet here proposed things that are not desirable, like storing the request body in a middleware 'for later reuse'. The code wasn't really working anyway.

@RobertoPrevato RobertoPrevato deleted the feature/asgi-middleware-wrapper branch February 8, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant