Im learning rust and I came across this sample code:
use actix_web::{middleware, web, App, HttpRequest, HttpServer};
async fn index(req: HttpRequest) -> &'static str {
println!("REQ: {req:?}");
"Hello world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
HttpServer::new(|| {
App::new()
// enable logger
.wrap(middleware::Logger::default())
.service(web::resource("/index.html").to(|| async { "Hello world!" }))
.service(web::resource("/").to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
As someone coming from Java, I dont know what this line of code means:
#[actix_web::main]
If I remove that and run the program, the error says 'main function cannot be async' , so it must be important.
Does anyone have link to documentation explaining what it is - the use of pound (#) sign?
This is an attribute. There are various kinds of attributes. In this case, this is an proc macro attribute, actix_web::main. It takes the item annotated with it (async fn main()) and transforms it into some other thing, usually some variation of the original code.
For example, this attribute prepares the Actix runtime and allows you to have an async main (in reality, main() cannot be async, but this attribute transforms it into something like fn main() -> ... { actix_web::rt::System::new().block_on(async move { ... }) }).
Related
Background
I need to create a couple of endpoints for an API service project. These API can accept encrypted and un-encrypted one (for development). Both parameters then are passed into a same function. For example:
/api/movie/get with encrypted parameter (for production)
/dev/movie/get with un-encrypted parameter (for development)
Current implementation in actix_web
I use actix_web and routes (not macro) to provide path routing. Modified from the sample code below
use actix_web::{web, App, HttpServer, Responder};
async fn index() -> impl Responder {
"Hello world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(
// prefixes all resources and routes attached to it...
web::scope("/app")
// ...so this handles requests for `GET /app/index.html`
.route("/index.html", web::get().to(index)),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
For each API endpoint, I had to create 2 functions, such as:
/// for encrypted one
/// PATH: /api/movie/get
pub async fn handle_get_movie(
app_data: Data<AppState>,
Json(payload): Json<EncryptedPayload>,
) -> Result<impl Responder, Error> {
// decrypt the content
// this is the only difference between un-encrypted and encrypted endpoint
let params = payload
.unload::<GetMovieInf>()
.map_err(|_| MainError::Malformatted)?;
// other codes here....
Ok(Json(ReplyInf {ok: true}))
}
/// for un-encrypted one
/// PATH: /dev/movie/get
pub async fn handle_get_movie(
app_data: Data<AppState>,
Json(payload): Json<UnencryptedPayload>,
) -> Result<impl Responder, Error> {
// other codes here. These codes are exactly identical with the above function...
Ok(Json(ReplyInf {ok: true}))
}
Questions
Since both functions are similar, does it possible to combine them both into a single function ? a function "overload" maybe ?
The problem is this line Json(payload): Json<UnencryptedPayload> in the parameter function. I tried to use generics like Json<T>. this doesn't work.
I can use the environment variable to control which should be active (EncryptedPayload or UnencryptedPayload). I can use one path for each endpoint (eg: /api/movie/get) and don't have to write the same functionality twice.
I'm currently working on an actix-web application but am having some difficulty while trying to refactor some of the code into smaller functions. When setting up the HttpServer the variable type is just inferred.
main.rs (basic example)
use actix_web;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// prepare the server variable but don't start it
let _server = actix_web::HttpServer::new(|| {
actix_web::App::new().service(nothing)
}).bind(("0.0.0.0", 31)).expect("FAIL");
// redacted other stuff
// ok now handle the server start
return _server.run().await;
}
#[actix_web::get("/")]
async fn nothing() -> actix_web::HttpResponse {
return actix_web::HttpResponse::Ok().body("nothing");
}
When moving it into a separate function though, I need to explicitly put the type definition so I can return it.
main.rs (refactored example)
use actix_web;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// prepare the server variable but don't start it
let _server = get_server();
// redacted other stuff
// ok now handle the server start
return _server.run().await;
}
#[actix_web::get("/")]
async fn nothing() -> actix_web::HttpResponse {
return actix_web::HttpResponse::Ok().body("nothing");
}
fn get_server() -> /* unknown type definition */ {
return actix_web::HttpServer::new(|| {
actix_web::App::new().service(nothing)
}).bind(("0.0.0.0", 31)).expect("FAIL");
}
I was wondering if there is a way to handle the type definition of the actix-web HttpServer when it cannot be inferred. I guess more generally I am wondering if it is possible to return an inferred variable type from a function but more specifically I'm just having difficulty defining the HttpServer type because of how many Generics it seems to need in its definition
I'm creating an actix-web application and I would like to register routes in a separate function. However, I cannot write a function that takes and returns App::new() since AppEntry is private:
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
let app = App::new();
register_routes(&app)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
fn register_routes(app: App<AppEntry>) -> App<AppEntry> {
App::new()
.route("/hey", web::get().to(hello))
}
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hey there!")
}
error[E0412]: cannot find type `AppEntry` in this scope
--> src/main.rs:14:29
|
14 | fn register_routes(app: App<AppEntry>) -> App<AppEntry> {
| - ^^^^^^^^ not found in this scope
| |
| help: you might be missing a type parameter: `<AppEntry>`
How should I write the register_routes function?
You can use App::configure for that, avoiding the need to pass an App<AppEntry> directly, like they show in this example.
By the way, in your register_routes function, you just create another, new App, you don't actually use the one that you pass in.
I'm trying to learn async programming, but this very basic example doesn't work:
use std::future::Future;
fn main() {
let t = async {
println!("Hello, world!");
};
t.poll();
}
Everything I've read from the specs says this should work, but cargo complains that method "poll" can't be found in "impl std::future::Future". What am I doing wrong?
poll has this signature:
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
There are two problems with calling this in the way you do:
poll is not implement on a future Fut, but on Pin<&mut Fut>, so you need to get a pinned reference first. pin_mut! is often useful, and if the future implements Unpin, you can use Pin::new as well.
The bigger problem however is that poll takes a &mut Context<'_> argument. The context is created by the asynchronous runtime and passed in to the poll function of the outermost future. This means that you can't just poll a future like that, you need to be in an asynchronous runtime to do it.
Instead, you can use a crate like tokio or async-std to run a future in a synchronous context:
// tokio
use tokio::runtime::Runtime;
let runtime = Runtime::new().unwrap();
let result = runtime.block_on(async {
// ...
});
// async-std
let result = async_std::task::block_on(async {
// ...
})
Or even better, you can use #[tokio::main] or #[async_std::main] to convert your main function into an asynchronous function:
// tokio
#[tokio::main]
async fn main() {
// ...
}
// async-std
#[async_std::main]
async fn main() {
// ...
}
I'm using actix-web to create a httpserver with state/data embedded in it. But vscode show me that the create_app function has wrong arguments in its return value type definition App<AppState>:
pub struct App<T, B>
wrong number of type arguments: expected 2, found 1
expected 2 type argumentsrustc(E0107)
app.rs:
use crate::api;
use crate::model::DbExecutor;
use actix::prelude::Addr;
use actix_web::{error, http::Method, middleware::Logger, web, App, HttpResponse};
pub struct AppState {
pub db: Addr<DbExecutor>,
}
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
App::new().data(AppState { db }).service(
web::resource("/notes/").route(web::get().to(api::notes))
);
}
main.rs:
fn main() {
HttpServer::new(move || app::create_app(addr.clone()))
.bind("127.0.0.1:3000")
.expect("Can not bind to '127.0.0.1:3000'")
.start();
}
As return type of "service" method is "Self" which is type actix_web::App, I tried modify return type to App (without generic parameter) but still got error, what should I do?
First, App takes two generic type arguments, App<AppEntry, Body>, you've only given one.
Second, AppState is not AppEntry.
Third, instantiating App outside actix-web is hard, if not impossible, as the types you need from actix-web are not public.
Instead, you should use configure to achieve the same, here is a simplified example:
use actix_web::web::{Data, ServiceConfig};
use actix_web::{web, App, HttpResponse, HttpServer};
fn main() {
let db = String::from("simplified example");
HttpServer::new(move || App::new().configure(config_app(db.clone())))
.bind("127.0.0.1:3000")
.expect("Can not bind to '127.0.0.1:3000'")
.run()
.unwrap();
}
fn config_app(db: String) -> Box<Fn(&mut ServiceConfig)> {
Box::new(move |cfg: &mut ServiceConfig| {
cfg.data(db.clone())
.service(web::resource("/notes").route(web::get().to(notes)));
})
}
fn notes(db: Data<String>) -> HttpResponse {
HttpResponse::Ok().body(["notes from ", &db].concat())
}
Read more about ServiceConfig in the api documentation.