/// Provides a RESTful web server managing some Todos. /// /// API will be: /// /// - `GET /todos`: return a JSON list of Todos. /// - `POST /todos`: create a new Todo. /// - `PUT /todos/:id`: update a specific Todo. /// - `DELETE /todos/:id`: delete a specific Todo. #[tokio::main] asyncfn main() { if env::var_os("RUST_LOG").is_none() { // Set `RUST_LOG=todos=debug` to see debug logs, // this only shows access logs.
env::set_var("RUST_LOG", "todos=info");
}
pretty_env_logger::init();
let db = models::blank_db();
let api = filters::todos(db);
// View access logs by setting `RUST_LOG=todos`. let routes = api.with(warp::log("todos")); // Start up the server...
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
mod filters { usesuper::handlers; usesuper::models::{Db, ListOptions, Todo}; use warp::Filter;
/// DELETE /todos/:id pubfn todos_delete(
db: Db,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone { // We'll make one of our endpoints admin-only to show how authentication filters are used let admin_only = warp::header::exact("authorization", "Bearer admin");
warp::path!("todos" / u64) // It is important to put the auth check _after_ the path filters. // If we put the auth check before, the request `PUT /todos/invalid-string` // would try this filter and reject because the authorization header doesn't match, // rather because the param is wrong for that other path.
.and(admin_only)
.and(warp::delete())
.and(with_db(db))
.and_then(handlers::delete_todo)
}
fn json_body() -> impl Filter<Extract = (Todo,), Error = warp::Rejection> + Clone { // When accepting a body, we want a JSON body // (and to reject huge payloads)...
warp::body::content_length_limit(1024 * 16).and(warp::body::json())
}
}
/// These are our API handlers, the ends of each filter chain. /// Notice how thanks to using `Filter::and`, we can define a function /// with the exact arguments we'd expect from each filter in the chain. /// No tuples are needed, it's auto flattened for the functions. mod handlers { usesuper::models::{Db, ListOptions, Todo}; use std::convert::Infallible; use warp::http::StatusCode;
pubasyncfn list_todos(opts: ListOptions, db: Db) -> Result<impl warp::Reply, Infallible> { // Just return a JSON array of todos, applying the limit and offset. let todos = db.lock().await; let todos: Vec<Todo> = todos
.clone()
.into_iter()
.skip(opts.offset.unwrap_or(0))
.take(opts.limit.unwrap_or(std::usize::MAX))
.collect();
Ok(warp::reply::json(&todos))
}
for todo in vec.iter() { if todo.id == create.id {
log::debug!(" -> id already exists: {}", create.id); // Todo with id already exists, return `400 BadRequest`. return Ok(StatusCode::BAD_REQUEST);
}
}
// No existing Todo with id, so insert and return `201 Created`.
vec.push(create);
let len = vec.len();
vec.retain(|todo| { // Retain all Todos that aren't this id... // In other words, remove all that *are* this id...
todo.id != id
});
// If the vec is smaller, we found and deleted a Todo! let deleted = vec.len() != len;
if deleted { // respond with a `204 No Content`, which means successful, // yet no body expected...
Ok(StatusCode::NO_CONTENT)
} else {
log::debug!(" -> todo id not found!");
Ok(StatusCode::NOT_FOUND)
}
}
}
mod models { use serde_derive::{Deserialize, Serialize}; use std::sync::Arc; use tokio::sync::Mutex;
/// So we don't have to tackle how different database work, we'll just use /// a simple in-memory DB, a vector synchronized by a mutex. pubtype Db = Arc<Mutex<Vec<Todo>>>;
pubfn blank_db() -> Db {
Arc::new(Mutex::new(Vec::new()))
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.