Comments (18)
For the same reasons you'd use a request guard otherwise: so that a handler's signature directly declares its requirements, and as a mechanism for centralizing policy. The former is straightforward, but the latter may seem irrelevant here. Let me explain.
Say you were to use r2d2 to get a connection via pool::get. The method returns a Result
, so you need to do something in the case of Err
. It's likely that you always want to do the same thing, and let's say that it's to return a 503 Service Unavailable if there is no connection available. A request guard lets you do this for every route that needs a connection, in one go, and it makes it so that the type is what is controlling the behavior. The type directly encapsulates what it means to have a connection or be unable to get one. Not only does this result in less code, but it also results in richer types.
On a more practical note, Rocket is likely to ship with a library for handling database connections in the near future, and it'll almost certainly use request guards for the reasons I mention above. Doing it this way as well, while there's no official library, will let you migrate to the official solution in short order. :)
from rocket.
Indeed, using lazy_static!
and RequestGuards seems a tad too much, especially if you have a lot of stuff to make available.
This is how I set up usage of a DB connection pool:
lazy_static! {
pub static ref DB_POOL: r2d2::Pool<ConnectionManager<PgConnection>> = {
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set.");
let config = r2d2::Config::default();
let manager = ConnectionManager::<PgConnection>::new(database_url);
let pool = r2d2::Pool::new(config, manager).expect("Failed to create pool.");
pool
};
}
pub struct DB(r2d2::PooledConnection<ConnectionManager<PgConnection>>);
impl DB {
pub fn conn(&self) -> &PgConnection {
&*self.0
}
}
impl<'a, 'r> FromRequest<'a, 'r> for DB {
type Error = r2d2::GetTimeout;
fn from_request(_: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
match DB_POOL.get() {
Ok(conn) => Success(DB(conn)),
Err(e) => Failure((Status::InternalServerError, e)),
}
}
}
The ceremonial practice of settings up the lazy_static!
and making it available as a RequestGuards could surely be abstracted away in to a higher level API which would make it much easier to do.
from rocket.
Managed state has now fully landed in master! Managed State is a feature composed of three items:
- A new
manage
method on aRocket
instance. - A new
State
type that acts as a request guard to retrieve managed state. - An
unmanaged_state
lint pass that warns whenState
is used for unmanaged state.
Here's an example on how to use these to have Rocket manage a start-up initialized configuration, from the State
documentation:
use rocket::State;
// In a real application, this would likely be more complex.
struct MyConfig(String);
#[get("/")]
fn index(state: State<MyConfig>) -> String {
format!("The config value is: {}", state.0)
}
#[get("/raw")]
fn raw_config_value<'r>(state: State<'r, MyConfig>) -> &'r str {
// use `inner()` to get a lifetime longer than `deref` gives us
state.inner().0.as_str()
}
fn main() {
let config = MyConfig("user input".to_string());
rocket::ignite()
.mount("/", routes![index, raw_config_value])
.manage(config)
.launch()
}
You can call manage
as many times as you'd like as long as each call passes a value of a different type. If you use a State<T>
for a T
that doesn't have a corresponding manage
call, the guard will return a 500 error to the client.
What I am most excited about is the unmanaged_state
lint, which protects you, in most cases, from using a State
guard for a type T
that isn't managed. When you do this, Rocket emits a warning that looks like the following:
warning: 'HitCount' is not currently being managed by Rocket, #[warn(unmanaged_state)] on by default
--> src/main.rs:16:21
|
16 | fn index(hit_count: State<HitCount>) -> content::HTML<String> {
| ^^^^^^^^^^^^^^^
|
help: maybe add a call to `manage` here?
--> src/main.rs:29:5
|
29 | rocket::ignite()
| ^^^^^^^^^^^^^^^^
= note: this 'State' request guard will always fail
warning: 'HitCount' is not currently being managed by Rocket, #[warn(unmanaged_state)] on by default
--> src/main.rs:24:21
|
24 | fn count(hit_count: State<HitCount>) -> String {
| ^^^^^^^^^^^^^^^
|
help: maybe add a call to `manage` here?
--> src/main.rs:29:5
|
29 | rocket::ignite()
| ^^^^^^^^^^^^^^^^
= note: this 'State' request guard will always fail
These warnings were captured by removing the manage
call from the state example, which shows how to use managed state to implement a hit counter.
I'll be writing a longer form article about managed state for the 0.2
release, which if all goes well, will be released tomorrow. As a result of managed state landing in earnest, I am closing this issue. I've opened #167 to track supporting connection pools in Rocket's contrib
.
from rocket.
@SergioBenitez: of course it does!
from rocket.
+1 for the difficulty in initializing state. I was playing with image and Rocket yesterday to implement a hit counter for some practice and got stuck on 'how am I going to initialize my counts?'
Maybe a good hitcounter example would be helpful to illustrate the 'right' way of sharing state between requests (I couldn't grok how the Cookie example guard worked under the hood).
WTR @PAStheLoD's question I'd be fine keeping all kinds of state Rocket routes needed within a single Mutexed struct (db pool, logging system, etc) and just name it MasterState or something. If Rocket's living inside a larger app, I'd just need references to what Rocket needed in there.
As an example from my weekend project, I need to store a memory cache of image tiles somewhere and initialize as well as access my counters. Being able to pass state to the request guards would be mighty handy, but in this example it would really just need a good way to be initialized.
from rocket.
Request Guards should fit the bill nicely. Did you have a chance to read through that section of the guide?
from rocket.
Just encountered this too. It's a pretty common issue in these frameworks, I think!
In Iron, I got around it by implementing my handlers as move ||
closures. For rocket, Request Guards seem like a good match for this, but even after reading that section of the guide it isn't immediately clear how to use them to return some shared piece of state data that is set up in main()
rather than derived from the client's Request.
from rocket.
I suppose one way to do this would be with some mutable data set up inside a lazy_static!
, possibly protected with a mutex, and an associated type whose from_request()
implementation just returned a reference to the lazy static data?
from rocket.
@hyena Yep!
Ideally, with time, the most common versions of something like this will be encapsulated by libraries, so you don't have to deal with this yourself. Template
, for example, does a bit of this for you. These libraries will live in the contrib
crate. The next addition is likely to be something that makes managing database connections as easy as returning a Template
.
from rocket.
Thanks for sanity checking that! :) I'll use that approach for now.
Looking ahead, do you think Rocket should stick with this approach or something else? Iron does a weird thing where Requests have a http://ironframework.io/doc/iron/typemap/struct.TypeMap.html that can have one object per type put into it, usually set up via middleware. But that doesn't seem like a clear match here (and the one object per type approach feels a little hacky too).
EDIT: The contrib/template approach makes sense. Something flexible that can be set up and associated with routes.
from rocket.
Rocket will definitely stick with this approach. It is part of what makes Rocket, Rocket. Something like TypeMap
will never appear in any official Rocket repositories. I don't believe dynamic typing should appear in a language like Rust, especially when you have code generation. That's not to say that there won't be some other mechanism that lets you do some things more easily or that is better suited to some tasks than request guards, however.
Request guards aren't meant to be a hack. They are designed exactly for use cases like this. You have some global state; it needs to be global. Unfortunately, there's no way around it. But request guards let you reason about that global state at a local type-level, which I think is awesome.
from rocket.
If I "just" were to use a lazy_static!
for controlling the db connection and connection pool, why would I use a RequestGuard instead of simply use super::DB
?
from rocket.
How would that look in a larger system (that uses and depends on a lot of external services, let's say a relational DB for customer data, emitting events to a firehose "global" event stream such as Kafka, logging metrics to something statsd-like, sending a transactional email, maybe even grabbing some other semi-internal services, one for User Authentication, and a different one for Request Authorization)?
Sure, encoding everything into types at the function level gives enormously powerful semantics, but maybe a bit too unwieldy syntax.
Anyway, maybe there's simply no great solution for handling these orthogonal concerns (at least I know of none), so far every library/crate/framework encodes some parts of itself through the type system (understandable, as that's the most obvious possibility in Rust), but usually people don't want to structure their programs like matyoskha dolls. (Everything would have to go inside one big block where let's say the r2d2 pool is available.)
Of course I could be misunderstanding the problem, so thank you in advance for any helpful reply!
from rocket.
How would that look in a larger system
It's an interesting question. I got a working solution to my problem with lazy_static!
and request_guards: https://github.com/hyena/book-of-stars/blob/master/src/main.rs#L92 but even from there it was clear that this would become unwieldly in a situation with too much initialization...
The problem from my perspective isn't an unwieldy syntax. If there's no initialization required, request_guards work fine for passing typed parameters to handlers. We could even implement our own trait that was a simplified form of FromRequest
.
The awkward part right now in my opinion is the initialization: How to, for example, setup a threadpool or a connection to logging metrics, etc. lazy_static!
and Once
can help but I personally find pushing too much logic into them to be awkward.
Perhaps what I'd really like to do is do the setup in main()
and do some registration as part the .launch()
call chain. The types and objects could still appear as parameters on the route handlers.
I'm still not so sure. Pyramid handles this with middleware and the ability to register a 'request property' that can reify a property on a request
object (e.g. https://github.com/Weasyl/weasyl/blob/master/weasyl/middleware.py#L164 + https://github.com/Weasyl/weasyl/blob/master/weasyl/wsgi.py#L40). But typing is less elegant here compared to a rocket's function parameters on the route handler.
The tl;dr is that the way request guards make global state available to route handlers seems like a good fit. But it feels awkward currently to setup global state and make it available to the request guards.
from rocket.
Thanks for the example @ChrisBuchholz! I also found this is how the Template extension was implemented.
from rocket.
The ceremonial practice of settings up the lazy_static! and making it available as a RequestGuards could surely be abstracted away in to a higher level API which would make it much easier to do.
We could build such an abstraction today. But I think there are two other requirements a solution should have:
-
The ability to set up objects in
main()
before igniting the rocket. Putting everything inlazy_static!
is a little ugly. -
Deciding how these will be made available to individual handlers.
In Pyramid (sorry to keep going back to that, but it's a useful comparison), a common approach to making things like connections available to handlers is to register them as named properties on the request object, setup by middleware before the handler sees them.
Rocket's current approach is somewhat different if I understand it correctly: We write a FromRequest implementation for a type and all handlers that use one of those types in its arguments uses that implementation to create one. They can even have multiple ones on a single handler.
Probably the best approach for now is to stick with Rocket's request guard style and see if we can wrap a new sort of trait around it, one that lets us initialize more easily at a non-static level.
from rocket.
The functionality described on this page for supporting things like connection pools is different than the State that was merged into master last week, correct?
from rocket.
@greglearns Yes and no. You can use managed state to hold the connection pool, but you'll still need to implement a request guard that fetches a connection instance from the pool. I'd like for this last step to be removed as well, even though it's a small one, since it's so common.
Still, I believe we can close this issue once managed state lands.
from rocket.
Related Issues (20)
- Can't change IP that Rocket starts from. HOT 1
- Clippy lint: temporary with significant `Drop` can be early dropped HOT 1
- Rocket sometimes resets connection instead of responding with 413 error response HOT 2
- MiniJinja can't be used for templates HOT 1
- Routing by hostname HOT 10
- New release of rocket_dyn_templates HOT 3
- Redirector issue in TLS example HOT 5
- Deployment Docs - Static Files HOT 1
- FromForm Option<Result> with custom type HOT 4
- Possible error in the Json guard HOT 3
- Allow Generics in route functions HOT 1
- Caching a background image with the `Cache-Control` header HOT 2
- sslmode=required not working for rocket_db_pool with feature diesel_postgres HOT 1
- Fairing returns incorrect Content-Length for status 204 HOT 7
- No way to turn off coloured log output HOT 2
- Implement derive FromParam for enum HOT 1
- Websockets (and any other connection upgrade) does not support local testing.
- `rocket_http` doesn't bulid/test on `rust` `1.80.0` HOT 1
- Expose the `tracing` layer construction to allow you to add other e.g. tracing_subscribers HOT 2
- Implement `PartialEq` for `http::Status` with derive macro HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rocket.