diff --git a/.gitignore b/.gitignore index 4a903f99..532edaeb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ Cargo.lock # intellij files .idea/** + +.history/ diff --git a/Cargo.toml b/Cargo.toml index 9ff86387..d6285920 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "actix_todo", "async_db", "async_ex1", + "async_ex2", "basics", "cookie-auth", "cookie-session", diff --git a/async_ex1/README.md b/async_ex1/README.md index 11bbbeed..1b446fc8 100644 --- a/async_ex1/README.md +++ b/async_ex1/README.md @@ -16,5 +16,4 @@ Actix-web features illustrated here include: Example query from the command line using httpie: - ```http post 127.0.0.1:8088/something id=1 name=JohnDoe``` - + ```echo '{"id":"1", "name": "JohnDoe"}' | http 127.0.0.1:8080/something``` diff --git a/async_ex1/src/main.rs b/async_ex1/src/main.rs index 30af79ed..7057e694 100644 --- a/async_ex1/src/main.rs +++ b/async_ex1/src/main.rs @@ -11,9 +11,7 @@ // 2. actix-web client features: // - POSTing json body // 3. chaining futures into a single response used by an asynch endpoint -// -// There are 2 versions in this example, one that uses Boxed Futures and the -// other that uses Impl Future, available since rustc v1.26. + #[macro_use] extern crate validator_derive; @@ -49,60 +47,10 @@ struct HttpBinResponse { url: String, } -// ----------------------------------------------------------------------- -// v1 uses Boxed Futures, which were the only option prior to rustc v1.26 -// ----------------------------------------------------------------------- + /// post json to httpbin, get it back in the response body, return deserialized -fn step_x_v1( - data: SomeData, - client: &Client, -) -> Box> { - Box::new( - client - .post("https://httpbin.org/post") - .send_json(&data) - .map_err(Error::from) // <- convert SendRequestError to an Error - .and_then(|resp| { - resp // <- this is MessageBody type, resolves to complete body - .from_err() // <- convert PayloadError to an Error - .fold(BytesMut::new(), |mut acc, chunk| { - acc.extend_from_slice(&chunk); - Ok::<_, Error>(acc) - }) - .map(|body| { - let body: HttpBinResponse = - serde_json::from_slice(&body).unwrap(); - body.json - }) - }), - ) -} - -fn create_something_v1( - some_data: web::Json, - client: web::Data, -) -> Box> { - Box::new( - step_x_v1(some_data.into_inner(), &client).and_then(move |some_data_2| { - step_x_v1(some_data_2, &client).and_then(move |some_data_3| { - step_x_v1(some_data_3, &client).and_then(|d| { - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(serde_json::to_string(&d).unwrap()) - .into()) - }) - }) - }), - ) -} - -// --------------------------------------------------------------- -// v2 uses impl Future, available as of rustc v1.26 -// --------------------------------------------------------------- - -/// post json to httpbin, get it back in the response body, return deserialized -fn step_x_v2( +fn step_x( data: SomeData, client: &Client, ) -> impl Future { @@ -123,17 +71,16 @@ fn step_x_v2( }) } -fn create_something_v2( +fn create_something( some_data: web::Json, client: web::Data, ) -> impl Future { - step_x_v2(some_data.into_inner(), &client).and_then(move |some_data_2| { - step_x_v2(some_data_2, &client).and_then(move |some_data_3| { - step_x_v2(some_data_3, &client).and_then(|d| { + step_x(some_data.into_inner(), &client).and_then(move |some_data_2| { + step_x(some_data_2, &client).and_then(move |some_data_3| { + step_x(some_data_3, &client).and_then(|d| { Ok(HttpResponse::Ok() .content_type("application/json") - .body(serde_json::to_string(&d).unwrap()) - .into()) + .body(serde_json::to_string(&d).unwrap())) }) }) }) @@ -142,19 +89,17 @@ fn create_something_v2( fn main() -> io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); + let endpoint = "127.0.0.1:8080"; + println!("Starting server at: {:?}", endpoint); HttpServer::new(|| { App::new() .data(Client::default()) .service( - web::resource("/something_v1") - .route(web::post().to(create_something_v1)), - ) - .service( - web::resource("/something_v2") - .route(web::post().to_async(create_something_v2)), + web::resource("/something") + .route(web::post().to_async(create_something)), ) }) - .bind("127.0.0.1:8088")? + .bind(endpoint)? .run() } diff --git a/async_ex2/Cargo.toml b/async_ex2/Cargo.toml new file mode 100644 index 00000000..d8195f6c --- /dev/null +++ b/async_ex2/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "async_ex2" +version = "0.1.0" +authors = ["dowwie "] +edition = "2018" +workspace = ".." + +[dependencies] +actix-rt = "0.2.2" +actix-web = { version="1.0.0-alpha.4", features=["ssl"] } +actix-multipart = { git="https://github.com/actix/actix-web.git" } +bytes = "0.4.12" +env_logger = "0.6.1" +futures = "0.1" +serde = { version = "^1.0", features = ["derive"] } +serde_derive = "1.0.90" +serde_json = "1.0.39" +time = "0.1.42" +validator = "0.8.0" +validator_derive = "0.8.0" diff --git a/async_ex2/src/README.md b/async_ex2/src/README.md new file mode 100644 index 00000000..68374f9a --- /dev/null +++ b/async_ex2/src/README.md @@ -0,0 +1,3 @@ +This example illustrates how to use nested resource registration through application-level configuration. +The endpoints do nothing. + diff --git a/async_ex2/src/appconfig.rs b/async_ex2/src/appconfig.rs new file mode 100644 index 00000000..ae23b09c --- /dev/null +++ b/async_ex2/src/appconfig.rs @@ -0,0 +1,47 @@ +use actix_web::{error, web}; +use bytes::Bytes; +use futures::Stream; + +use crate::{ + handlers::{ + products, + parts + }, +}; + + +pub fn config_app

(cfg: &mut web::RouterConfig

) + where P: Stream + + 'static +{ + // domain includes: /products/{product_id}/parts/{part_id} + cfg.service( + web::scope("/products") + .service( + web::resource("") + .route(web::get().to_async(products::get_products)) + .route(web::post().to_async(products::add_product)) + ) + .service( + web::scope("/{product_id}") + .service( + web::resource("") + .route(web::get().to_async(products::get_product_detail)) + .route(web::delete().to_async(products::remove_product)) + ) + .service( + web::scope("/parts") + .service( + web::resource("") + .route(web::get().to_async(parts::get_parts)) + .route(web::post().to_async(parts::add_part)) + ) + .service( + web::resource("/{part_id}") + .route(web::get().to_async(parts::get_part_detail)) + .route(web::delete().to_async(parts::remove_part)) + ) + ) + ) + ); +} \ No newline at end of file diff --git a/async_ex2/src/bin/main.rs b/async_ex2/src/bin/main.rs new file mode 100644 index 00000000..44c9c7e7 --- /dev/null +++ b/async_ex2/src/bin/main.rs @@ -0,0 +1,19 @@ +use actix_web::{middleware, App, HttpServer}; + +use async_ex2::{ + appconfig::config_app, +}; + + +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); + env_logger::init(); + + HttpServer::new(|| + App::new() + .configure(config_app) + .wrap(middleware::Logger::default()) + ) + .bind("127.0.0.1:8080")? + .run() +} \ No newline at end of file diff --git a/async_ex2/src/common.rs b/async_ex2/src/common.rs new file mode 100644 index 00000000..4359c2ec --- /dev/null +++ b/async_ex2/src/common.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + + +#[derive(Deserialize, Serialize)] +pub struct Product { + id: Option, + product_type: Option, + name: Option, +} + + +#[derive(Deserialize, Serialize)] +pub struct Part { + id: Option, + part_type: Option, + name: Option, +} \ No newline at end of file diff --git a/async_ex2/src/handlers/mod.rs b/async_ex2/src/handlers/mod.rs new file mode 100644 index 00000000..17f9e2c2 --- /dev/null +++ b/async_ex2/src/handlers/mod.rs @@ -0,0 +1,3 @@ +pub mod products; +pub mod parts; + diff --git a/async_ex2/src/handlers/parts.rs b/async_ex2/src/handlers/parts.rs new file mode 100644 index 00000000..22bb017a --- /dev/null +++ b/async_ex2/src/handlers/parts.rs @@ -0,0 +1,26 @@ +use actix_multipart::{Field, Item, Multipart, MultipartError}; +use actix_web::{HttpResponse, web, error, Error}; +use futures::{future::{ok as fut_ok, err as fut_err, Either}, Future, Stream}; + +use crate::common::{Part, Product}; + + +pub fn get_parts(query: web::Query>) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} + +pub fn add_part(new_part: web::Json) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} + +pub fn get_part_detail(id: web::Path) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} + +pub fn remove_part(id: web::Path) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} \ No newline at end of file diff --git a/async_ex2/src/handlers/products.rs b/async_ex2/src/handlers/products.rs new file mode 100644 index 00000000..5d0cd2a9 --- /dev/null +++ b/async_ex2/src/handlers/products.rs @@ -0,0 +1,26 @@ +use actix_multipart::{Field, Item, Multipart, MultipartError}; +use actix_web::{HttpResponse, web, error, Error}; +use futures::{future::{ok as fut_ok, err as fut_err, Either}, Future, Stream}; + +use crate::common::{Part, Product}; + + +pub fn get_products(query: web::Query>) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} + +pub fn add_product(new_product: web::Json) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} + +pub fn get_product_detail(id: web::Path) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} + +pub fn remove_product(id: web::Path) + -> impl Future { + fut_ok(HttpResponse::Ok().finish()) +} \ No newline at end of file diff --git a/async_ex2/src/lib.rs b/async_ex2/src/lib.rs new file mode 100644 index 00000000..1e9c00b3 --- /dev/null +++ b/async_ex2/src/lib.rs @@ -0,0 +1,3 @@ +pub mod appconfig; +pub mod common; +pub mod handlers;