Skip to content

How to Apply Multiple Middleware Layers (e.g., Auth + Custom Filter) on the Same Route? #1158

@qinwenhui

Description

@qinwenhui

I need to apply ​​both an authentication handler​​ (via auth_required=True) and a ​​custom pre-processing middleware​​ (via @before_request) to the same route (e.g., /index). For example:

​​Authentication​​: Validate permissions through the BasicAuthHandler.

​​Custom Filter​​: Execute non-auth tasks like request logging or input sanitization.

But when combined, they cause a route conflict. How can I achieve this in Robyn?

Code Attempt

@app.get("/index", auth_required=True) 
async def index(request):
    return "Index Page"

@app.before_request("/index")
async def custom_filter(request: Request):
    print("Checking request headers...")
    return request

Error Observed:​

thread '<unnamed>' panicked at src\server.rs:389:14:
called `Result::unwrap()` on an `Err` value: insertion failed due to conflict with previously registered route: /index
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Questions:
​​Design Philosophy​​: Does Robyn intentionally limit one middleware per route, or is this a configuration issue?

If applicable, could features like ​​middleware priority ordering​​ or ​​route-scoped middleware grouping​​ be added? Example:

# Hypothetical API
@app.get("/secure")
@app.middleware(
    AuthMiddleware(), 
    CustomFilterMiddleware(), 
    order=["auth", "filter"]
)
async def secure_route(request):
    return "Secure Data"

and...

I noticed the following code in the implementation:

def add_auth_middleware(self, endpoint: str):
    """
    This method adds an authentication middleware to the specified endpoint.
    """
    injected_dependencies: dict = {}

    def decorator(handler):
        @wraps(handler)
        def inner_handler(request: Request, *args):
            if not self.authentication_handler:
                raise AuthenticationNotConfiguredError()
            identity = self.authentication_handler.authenticate(request)
            if identity is None:
                return self.authentication_handler.unauthorized_response
            request.identity = identity
            return request

        self.add_route(
            MiddlewareType.BEFORE_REQUEST,
            endpoint,
            inner_handler,
            injected_dependencies,
        )
        return inner_handler
    return decorator

This implies that the ​​authentication handler is inherently registered as a MiddlewareType.BEFORE_REQUEST middleware​​. Therefore, when combining auth_required=True with custom @app.before_request on the same route, we're essentially creating ​​two BEFORE_REQUEST middlewares for the same endpoint​​, leading to conflicts.

To verify this hypothesis, I tried registering multiple ​​@before_request handlers on the same endpoint​​:

@app.before_request("/index")
async def hello_before_request(request: Request):
    print("before_request1111111111111...")
    print("Custom filtering processing, unrelated to permissions....")
    return request

@app.before_request("/index")
async def hello_before_request2(request: Request):
    print("before_request2222222222222...") 
    print("Multiple filtering processes...")
    return request

This indeed triggered the same error:

called `Result::unwrap()` on an `Err` value: 
insertion failed due to conflict with previously registered route: /index

This confirms that ​​Robyn's current architecture doesn't allow multiple BEFORE_REQUEST middlewares on the same route​​, whether they're authentication handlers or custom middlewares??

How should I handle it...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions