I have a simple middleware intended to access the application's global state to perform validation of the authentication token:
use actix_web;
use actix_web::HttpMessage;
pub struct Authenticator;
impl<S> actix_web::middleware::Middleware<S> for Authenticator {
fn start(
&self,
request: &mut actix_web::HttpRequest<S>,
) -> actix_web::Result<actix_web::middleware::Started> {
//let _state = request.state() as &::application::State;
match request.headers().get("Authentication") {
Some(_) => Ok(actix_web::middleware::Started::Done),
None => Err(::view::error(
"No authentication header provided",
actix_web::http::StatusCode::FORBIDDEN,
)),
}
}
}
The commented string shows how I tried to get the state. I have tried many ways actually. What is the best way of doing such stuff?
I thought about adding a reference to needed data (e.g. Arc'd RwLock) to the Authenticator structure and constructing it with the reference when I register my middlewares.
I am still not good with trait stuff, but there must be a clean way of casting the S type to my application-defined State structure:
pub struct State {
pub database: actix::Addr<actix::Syn, ::database::Actor>,
pub cache: ::std::sync::Arc<::cache::Cache>,
pub sessions: ::std::sync::Arc<::session::Storage>,
}
Use your state instead of S:
impl actix_web::middleware::Middleware<::Application::State> for Authenticator {
}
By the way, middleware can have state too.
Related
I initialized a Mutex<Pool<Postgres>> instance and passed it to manage method on Rocket instance in order to access the database on my controllers. What I usually do is:
// this is purely for example
#[get("/foo/bar")]
pub async fn controller<'a>(
// ... others ...
db_pool: &State<Mutex<PgPool>>,
// ... others ...
) {
// ... do other things ...
let query_r = sqlx::query("SELECT * FROM users WHERE id=$1")
.bind(&id)
.fetch_optional(&mut db_pool.inner().lock().await.acquire().await.unwrap()) // i reach `inner` of state, then `lock` the mutex, lastly `acquire` a pool connection
.await; // type is Result<Option<PgRow>> i suppose, rust-analyzer is not very good at inferring it
// ... do other things ...
}
This is cool and all but here's my problem: I wrote a struct as below...
pub struct User {
id: usize,
username: String,
email: String,
}
...and I actually want to use it as request guard so that the client cannot hit the controllers if they haven't provided the correct credentials.
Official guide of Rocket tells to implement FromRequest trait so that it can be resolved as guard.
However, in order to initialize a User instance, I need to get a PoolConnection inside this FromRequest trait. I have dived into Rocket's API documentation and seems like request parameter on FromRequest has a method named rocket with returns a Rocket<Orbit> instance, and, with this, I can access the state.
However, the problem is that state method returns &Mutex<Pool<Postgres>>, which is an immutable reference, so I cannot do any operations on database with this.
I have come this far and would like to ask if anyone knows a way to access Mutex<Pool<Postgres>> without a reference.
Thanks in advance.
How Far I Have Come
Below is how I have implemented FromRequest.
pub struct User {
id: usize,
username: String,
email: String,
}
#[derive(Debug)]
pub enum UserError {
TokenError(TokenError),
MissingToken,
DatabasePoolError,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = UserError;
async fn from_request(request: &'r rocket::Request<'_>) -> request::Outcome<Self, Self::Error> {
let encrypted_token_o = request.headers().get_one("Authorization");
match encrypted_token_o {
Some(encrypted_token) => {
let claims_r = Claims::try_from(encrypted_token.to_owned());
match claims_r {
Ok(claims) => {
let db_pool_o = request.rocket().state::<Mutex<PgPool>>();
match db_pool_o {
Some(db_pool) => {
let id = 0; // a test id
let query_r = sqlx::query(
"SELECT id, username, email FROM users WHERE id=$1",
)
.bind(&id)
.fetch_optional(
// ERROR: no method named `inner` found for reference `&rocket::futures::lock::Mutex<Pool<Postgres>>` in the current scope
&mut db_pool.inner().lock().await.acquire().await.unwrap(),
)
.await;
todo!()
}
None => request::Outcome::Failure((
http::Status::InternalServerError,
UserError::DatabasePoolError,
)),
}
}
Err(e) => request::Outcome::Failure((
http::Status::InternalServerError,
UserError::TokenError(e),
)),
}
}
None => request::Outcome::Failure((http::Status::BadRequest, UserError::MissingToken)),
}
}
}
Environment
rustc 1.55.0
rocket 0.5.0-rc.1 with features default and json
sqlx 0.5.7 with features runtime-tokio-native-tls, all-types, postgres and migrate
When I tried to build an application with axum, I failed to separate the framework from my handler. With Go, the classic way is define an Interface, implement it and register the handler to framework. In this way, it's easy to provide a mock handler to test with. However, I couldn't make it work with Axum. I defined a trait just like above, but it wouldn't compile:
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use serde_derive::{Serialize, Deserialize};
use serde_json::json;
use axum::{Server, Router, Json};
use axum::extract::Extension;
use axum::routing::BoxRoute;
use axum::handler::get;
#[tokio::main]
async fn main() {
let app = new_router(
Foo{}
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
trait Handler {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse{ message: "It works.".to_owned()})
}
}
fn new_router<T:Handler>(handler: T) -> Router<BoxRoute> {
Router::new()
.route("/", get(helper))
.boxed()
}
fn helper<T:Handler>(
Extension(mut handler): Extension<T>,
Json(req): Json<GetRequest>
) -> Json<GetResponse> {
Json(handler.get(req).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
struct GetRequest {
// omited
}
#[derive(Debug, Serialize, Deserialize)]
struct GetResponse {
message: String
// omited
}
error[E0599]: the method `boxed` exists for struct `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>`, but its trait bounds were not satisfied
--> src/router.rs:25:10
|
25 | .boxed()
| ^^^^^ method cannot be called on `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>` due to unsatisfied trait bounds
|
::: /Users/lebrancebw/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.2.5/src/routing/mod.rs:876:1
|
876 | pub struct Layered<S> {
| --------------------- doesn't satisfy `<_ as tower_service::Service<Request<_>>>::Error = _`
|
= note: the following trait bounds were not satisfied:
`<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>> as tower_service::Service<Request<_>>>::Error = _`
I guess the key point is my design is apparently not "rustic". Is there a way to structure an Axum project that lends itself to testing easily?
The question is what you want to test. I will assume that you have some core logic and an HTTP layer. And you want to make sure that:
requests are routed correctly;
requests are parsed correctly;
the core logic is called with the expected parameters;
and return values from the core are correctly formatted into HTTP responses.
To test it you want to spawn an instance of the server with the core logic mocked out.
#lukemathwalker in his blog and book "Zero To Production In Rust" has a very nice description of how to spawn an app for testing through the actual TCP port. It is written for Actix-Web, but the idea applies to Axum as well.
You should not use axum::Server::bind, but rather use axum::Server::from_tcp to pass it a std::net::TcpListner which allows you to spawn a test server on any available port using `TcpListener::bind("127.0.0.1:0").
To make the core logic injectable (and mockable) I declare it as a struct and implement all the business methods on it. Like this:
pub struct Core {
public_url: Url,
secret_key: String,
storage: Storage,
client: SomeThirdPartyClient,
}
impl Core {
pub async fn create_something(
&self,
new_something: NewSomething,
) -> Result<Something, BusinessErr> {
...
}
With all these pieces you can write a function to start the server:
pub async fn run(listener: TcpListener, core: Core)
This function should encapsulate things like routing configuration, server logging configuration and so on.
Core can be provided to handlers using Extension Layer mechanism like this:
...
let shared_core = Arc::new(core);
...
let app = Router::new()
.route("/something", post(post_something))
...
.layer(AddExtensionLayer::new(shared_core));
Which in a handler can be declared in parameter list using extension extractor:
async fn post_something(
Extension(core): Extension<Arc<Core>>,
Json(new_something): Json<NewSomething>,
) -> impl IntoResponse {
core
.create_something(new_something)
.await
}
Axum examples contain one on error handling and dependency injection. You can check it here.
Last but not least, now you can mock Core out with a library like mockall, write spawn_app function that would return host and port where the server is run, run some requests against it and do assertions.
The video from Bogdan at Let's Get Rusty channel provides a good start with mockall.
I will be happy to provide more details if you feel something is missing from the answer.
I am making an authorization system which will implement the FromRequest trait from the web framework rocket, which is needed to make custom guards.
The first problem I have gone into is how I have to make the connection global. Should I cast it as a constant, so it can be accessed from the functions in the implementation, or is there a cache in rocket or some form of storage where the PgPool connection (since I am using sqlx) can be accessed & queries can be made.
The second problem I have gone through is making the FromRequest functions asynchronous. Since sqlx is natively async afaik, I do not know if rocket has yet to support this. I was thinking of either making a tokio thread, or if there is a version of .then() in Rust, but I do not know
#[derive(Debug)]
struct User {
username: String,
password: String,
user_id: i16,
phone_number: String,
display_name: String,
//other fields omitted
}
impl<'a, 'r> FromRequest<'a, 'r> for &'a User {
type Error = !;
fn from_request(request: &'a Request<'r>) -> request::Outcome<&'a User, !> {
let user_result = request.local_cache(|| {
//..need to fetch user with sqlx [but sqlx is async]
//and later get cookies and check
});
user_result.as_ref().or_forward(())
}
}
Async support for Rocket lands in Version 0.5. For now, you can use the master branch: https://github.com/SergioBenitez/Rocket
The idiomatic way to handle this use case in Rocket would be to use rocket-contrib adapter for databases: https://docs.rs/rocket_contrib/0.4.7/rocket_contrib/databases/index.html#provided
You'll need to implement the Poolable Trait for your database. Here are the docs for that: https://docs.rs/rocket_contrib/0.4.7/rocket_contrib/databases/trait.Poolable.html
Rocket already provides the trait for the following:
diesel::MysqlConnection
diesel::PgConnection
diesel::SqliteConnection
postgres::Connection
mysql::Conn
rusqlite::Connection
rusted_cypher::GraphClient
redis::Connection
You can then use the database macro like so:
use rocket_contrib::databases::postgres;
#[database("db_name")]
struct MyPgDatabase(postgres::Connection);
fn main() {
rocket::custom(config)
.attach(MyPgDatabase::fairing())
.launch();
}
You can then just use this type as a request guard and then retrieve the database connection since the macro above automatically generates the FromRequest implementation for you
#[get("/")]
fn my_handler(conn: MyPgDatabase) {
// ...
}
The macro also generates a Deref implementation allowing you access to the inner connection type.
For Reference:
https://github.com/TatriX/realworld-rust-rocket: A great code example showing this
I'm using Actix and Diesel. The way I set it up is that I have a DbExecutor - an async worker which takes messages and returns the results of the queries they represent, like so:
#[derive(Message)]
#[rtype(result = "Result<User>")]
pub struct RetrieveUser {
pub uid: i64,
}
impl Handler<RetrieveUser> for DbExecutor {
// ...
fn handle(&mut self, msg: InsertFile, _: &mut Self::Context) -> Self::Result {
use crate::schema::users::dsl::*;
// ...
Ok(users.filter(uid.eq(msg.uid)).execute(connection))
}
}
fn my_route_handler() {
db_executor.send(RetrieveUser{ uid: 123 });
//...
}
However, there is a table which doesn't fit neatly into these simple queries, and I would like to specify the filters from the side of the message sender. I was thinking I could send a message containing a closure which takes the users object, and then execute it on the side of the DbExecutor, returning the results:
fn my_ideal_route_handler(){
db_executor.send(RetrieveUsers { predicate: |users| users.filter(uid.eq(123)) });
}
However, I am stuck, seeing as all Messages have to implement Copy, and the Diesel types are confusing - I am not sure how to specify the type I need generically enough, while getting it to compile, eg.:
// FAILS
#[derive(Message)]
#[rtype(result = "Result<Vec<User>>")]
pub struct RetrieveUsers {
pub predicate: dyn FnOnce(dyn QueryDsl) -> dyn RunQueryDsl<PgConnection>,
}
I am also not sure whether this approach is even sensible at all, so I am open to other suggestions which would:
allow me to query the table dynamically
still keep most of the database handling separate and not all across the project as if I were passing the plain database connection around
(ideally) utilize concurrent access to the database
When creating a struct in Rust it seems like it's difficult to create one without having all of the fields set. For example with the following code
struct Connection {
url: String,
stream: TcpStream
}
You aren't able to set url without giving stream as well.
// Compilation error asking for 'stream'
let m = Connection { url: "www.google.com".to_string() };
How are you able to create these references that might be Option<None> until a later time?
The best I have found is using the Default trait, but I'd rather not have to create the TcpStream until a later time than when the struct is initialised. Am I able to do this with something like a Box?
One thing you can do is to wrap the TcpStream in an Option, i.e. Option<TcpStream>. When you first construct the struct, it'll be None, and when you initialize it you make it self.stream = Some(<initialize tcp stream>). Wherever you use the TCPStream, you'll have to check if it's Some, i.e. if it has already been initialized. If you can guarantee your behavior then you can just unwrap(), but it's probably better to make a check anyways.
struct Connection {
url: String,
stream: Option<TcpStream>
}
impl Connection {
pub fn new() -> Connection {
Connection {
url: "www.google.com".to_string(),
stream: None,
}
}
pub fn initialize_stream(&mut self) {
self.stream = Some(TcpStream::connect("127.0.0.1:34254").unwrap());
}
pub fn method_that_uses_stream(&self) {
if let Some(ref stream) = self.stream {
// can use the stream here
} else {
println!("the stream hasn't been initialized yet");
}
}
}
This is similar to what is done in Swift, in case you're familiar with that language.
All fields indeed have to be initialized when creating the struct instance (there is no null in Rust) so all the memory is allocated.
There is often a dedicated method (like new) that sets default values for fields which are supposed to be modified at a later stage.
I'd use the Box when you don't know the size of the field (like Vec does).
As an extension to Jorge Israel Peña's answer, you can use a builder. The builder has all the optional fields and produces the final value without Options:
use std::net::TcpStream;
struct ConnectionBuilder {
url: String,
stream: Option<TcpStream>,
}
impl ConnectionBuilder {
fn new(url: impl Into<String>) -> Self {
Self {
url: url.into(),
stream: None,
}
}
fn stream(mut self, stream: TcpStream) -> Self {
self.stream = Some(stream);
self
}
fn build(self) -> Connection {
let url = self.url;
let stream = self
.stream
.expect("Perform actual error handling or default value");
Connection { url, stream }
}
}
struct Connection {
url: String,
stream: TcpStream,
}
impl Connection {
fn method_that_uses_stream(&self) {
// can use self.stream here
}
}
This means that you don't have to litter your code with checks to see if the stream has been set yet.
See also:
How to initialize a struct with a series of arguments
Do Rust builder patterns have to use redundant struct code?
Is it possible to create a macro to implement builder pattern methods?
How to write an idiomatic build pattern with chained method calls in Rust?