actix / actix-web

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
https://actix.rs
Apache License 2.0
21.6k stars 1.67k forks source link

multiple handler routing / "Next" responder #1425

Open ramiroaisen opened 4 years ago

ramiroaisen commented 4 years ago

It's there a way to pass a request to the next handler? Something like

async fn handler() -> impl Responder {
  if some_condition() {
    Response::Ok()
  } else {
    Next
  }
}
robjtede commented 4 years ago

What would the “next handler” mean exactly?

ramiroaisen commented 4 years ago

What would the “next handler” mean exactly?

It would mean that the request continues the handling process as if it did not match the handler something like the (req, res, next <--) function in express (nodejs) if you know waht i mean.

robjtede commented 4 years ago

Alright, just checking there was a distinction here against the other semantic in Node land for next (which, in my experience with Koa, was yielding control to inner middlewares).

There's been a couple discussion in various issues that have mentioned having multiple handlers be able to answer the same query. Some of the discussions have been around guard matching but I like the idea of the express-like system where you can use "fallback handlers".

Just needs to pass request to the next out of the list of other, resource-matching handlers if the primary handler declares, by way of returning Next, that it isn't able to handle the current request. (It appears that current implementation will only service the last defined handler for a resource path but its plausible to change that.)

I see a couple issues that need to be addressed for any kind of multi-handler system to be considered viable:

Interested to hear your thoughts @ramiroaisen and even @actix/contributors?

ramiroaisen commented 4 years ago

the next solution also brings the posibility to match two different string formats in the same slot of the path, the handler then check if the format is the required and if not just pass it to the next handler

What happens when the last or only handler of a route returns Next, should it be treated as a 404, a 400, maybe a 500? The semantics of what Next means for every possibility should be clearly defineable and potentially recoverable/configurable with app data.

I think the better solution if there's no more handlers if just return an empty 404,

How should ownership be handled? Obviously Rust's mutability and ownership rules are much tighter than Node's. It would be very easy to end up in situations where parts of the request are consumed or modified in the first handler but are expected to work in the second handler, with the full request. I can think of a few approaches for handling this but all have drawbacks for now.

I think it can be done if the handler returns ownership to the system something like this:

async fn handler(request: Request) -> impl Responder {
  if some_condition() {
    Response::Ok()
  } else {
    Next(request)
  }
}
robjtede commented 4 years ago

Interested to get @actix/contributors take on this?

shakyShane commented 3 years ago

I'm also seeing a need for this.

My specific use-case is wanting to stack services

.service(Files::new("/", "dir1"))
.service(Files::new("/", "dir2"))
.service(Proxy::new("https://example.com"))

Where the first 2 handlers would next if they cannot serve a specific file from disk (instead of erroring/404'in) finally ending up with a proxy services taking over the request.

fakeshadow commented 3 years ago

This is not useful or easy to do with built in services types of actix-web. The service chain in actix-web always want a ServiceRequest in and ServiceResponse out. Adding addional types to input or output would be major break change and additional overhead for everyone to pay for no matter they use it or not.

You can always build your own service types for nested service logic. Like Files<Files<Proxy>> would easily achieve what you want and it acts very similar to middlewares anyway(The difference would be with custom nested services you can have self defined input output type).

TLDR: The built in service types of actix-web is to satisfy majority usage with a stable pattern and you can always customize services easily for your advanced usage.

ModProg commented 2 years ago

I have something somewhat related, but I could do it if I could access a Database in app_data from a guard.