Open lnicola opened 5 years ago
Well, there are a few things to unpack here.
ExtractFuture
isn't a realFuture
, so using the combinators doesn't work
This is true and I think it is a mistake. Future versions of tower-web may be able to get rid of ExtractFuture
in favor of a real future.
it's a bit hard to discover (you also need to call
poll
andextract
)
I agree, we probably should work on making the API more discoverable. The extract context is passed around, so maybe that value could have an extract::Context::extract(&self)
function. That might make it more discoverable. You would then do:
impl<B: BufStream> Extract<B> for User {
type Future = Box<Future<Item = Self, Error = extract::Error>>;
fn extract(context: &Context) -> Self::Future {
Box::new(context.extract::<DbConnection>().and_then(|conn| {
// ...
}))
}
}
This still isn't great... Part of the problem is Extract
conflates extracting from the request head and extracting from the body. I wonder if that can be split up. So, there would be an Extract
trait and an ExtractBody
trait.
Another option would be to use a macro strategy as well...
#[web(Extract)]
impl User {
fn extract(x_auth_token: String, db: DbConnection) -> impl Future<Item = User> {
db.find_user(x_auth_token)
}
}
if a route wants both a
DbConnection
and aUser
, you end up connecting to the database twice.
Definitely tricky, but there are options. extract::Context
could be used to cache extracted values on a per-request basis. Do you have suggestions for specific strategies?
I had another thought. I wonder if it would be possible to make this work somehow:
#[web(extract(cached))]
impl DbConnection {
fn extract(config: &Config) -> impl Future<Item = Self> {
DbConnection::connect(config)
}
}
#[web(extract)]
impl User {
fn extract(x_auth_token: String, db: &DbConnection) -> impl Future<Item = User> {
db.find_user(x_auth_token)
}
}
I'm not 100% sure what the exact attributes should be and how to make it work...
Since
Extract
seems to be the way to handle stuff like authentication, consider an application with two implementations of it:DbConnection
which is a database connectionUser
, which is an authenticated userImplementing
Extract
forDbConnection
is more or less fine: you can stash a database pool in aConfig
struct, retrieve it from theContext
, and request a new connection.The
User
implementation, however, requires a database connection. You can get one with<DbConnection as Extract<B>>::extract(context)
, however there are some disadvantages:poll
andextract
)ExtractFuture
isn't a realFuture
, so using the combinators doesn't workDbConnection
and aUser
, you end up connecting to the database twiceIs there a better solution to this? Maybe middlewares?