diff --git a/actix-web-lab-derive/Cargo.toml b/actix-web-lab-derive/Cargo.toml index 914bd890..69ce528e 100644 --- a/actix-web-lab-derive/Cargo.toml +++ b/actix-web-lab-derive/Cargo.toml @@ -26,7 +26,7 @@ syn = { version = "1", features = ["full", "parsing"] } [dev-dependencies] actix-test = "0.1.0-beta.13" actix-web = "4.0.0" -actix-web-lab = "=0.16.0" +actix-web-lab = "=0.16.1" futures-util = { version = "0.3.7", default-features = false, features = ["std"] } trybuild = "1" diff --git a/actix-web-lab/CHANGELOG.md b/actix-web-lab/CHANGELOG.md index adee4947..70caa451 100644 --- a/actix-web-lab/CHANGELOG.md +++ b/actix-web-lab/CHANGELOG.md @@ -1,7 +1,11 @@ # Changelog ## Unreleased - 2022-xx-xx + + +## 0.16.1 - 2022-04-23 - `Query` extractor now supports collecting multi-value items into a `Vec`. +- Add `derive` crate feature (on-by-default) that enables derive macros. ## 0.16.0 - 2022-04-11 diff --git a/actix-web-lab/Cargo.toml b/actix-web-lab/Cargo.toml index 7d2dbca3..13590067 100644 --- a/actix-web-lab/Cargo.toml +++ b/actix-web-lab/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-lab" -version = "0.16.0" +version = "0.16.1" description = "In-progress extractors and middleware for Actix Web" authors = ["Rob Ede "] keywords = ["actix", "http", "web", "framework", "async"] @@ -20,17 +20,18 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -nightly = [] +default = ["derive"] +derive = ["actix-web-lab-derive"] [dependencies] +actix-web-lab-derive = { version = "=0.16.0", optional = true } + actix-web = { version = "4", default-features = false } actix-http = "3" actix-service = "2" actix-router = "0.5" actix-utils = "3" actix-files = "0.6" - -actix-web-lab-derive = "=0.16.0" ahash = "0.7" arc-swap = "1.1" async-trait = "0.1" @@ -54,8 +55,9 @@ tokio = { version = "1.13.1", features = ["sync", "macros"] } tracing = { version = "0.1.30", features = ["log"] } [dev-dependencies] -actix-web = { version = "4", features = ["rustls"] } +actix-web-lab-derive = "=0.16.0" +actix-web = { version = "4", features = ["rustls"] } base64 = "0.13" ed25519-dalek = "1.0.1" env_logger = "0.9" diff --git a/actix-web-lab/README.md b/actix-web-lab/README.md index 307eaba7..c438c645 100644 --- a/actix-web-lab/README.md +++ b/actix-web-lab/README.md @@ -3,10 +3,10 @@ > Experimental extractors, middleware, and other extras for possible inclusion in Actix Web. [![crates.io](https://img.shields.io/crates/v/actix-web-lab?label=latest)](https://crates.io/crates/actix-web-lab) -[![Documentation](https://docs.rs/actix-web-lab/badge.svg)](https://docs.rs/actix-web-lab/0.16.0) +[![Documentation](https://docs.rs/actix-web-lab/badge.svg)](https://docs.rs/actix-web-lab/0.16.1) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web-lab.svg)
-[![dependency status](https://deps.rs/crate/actix-web-lab/0.16.0/status.svg)](https://deps.rs/crate/actix-web-lab/0.16.0) +[![dependency status](https://deps.rs/crate/actix-web-lab/0.16.1/status.svg)](https://deps.rs/crate/actix-web-lab/0.16.1) [![Download](https://img.shields.io/crates/d/actix-web-lab.svg)](https://crates.io/crates/actix-web-lab) [![CircleCI](https://circleci.com/gh/robjtede/actix-web-lab/tree/main.svg?style=shield)](https://circleci.com/gh/robjtede/actix-web-lab/tree/main) @@ -16,54 +16,54 @@ ### Responders -- `Csv`: efficient CSV streaming [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/respond/struct.Csv.html) -- `NdJson`: efficient NDJSON streaming [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/respond/struct.NdJson.html) -- `DisplayStream`: efficient line-by-line `Display` streaming [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/respond/struct.DisplayStream.html) -- `Html`: basic string wrapper that responds with HTML Content-Type [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/respond/struct.Html.html) +- `Csv`: efficient CSV streaming [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/respond/struct.Csv.html) +- `NdJson`: efficient NDJSON streaming [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/respond/struct.NdJson.html) +- `DisplayStream`: efficient line-by-line `Display` streaming [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/respond/struct.DisplayStream.html) +- `Html`: basic string wrapper that responds with HTML Content-Type [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/respond/struct.Html.html) ### Middleware -- `from_fn`: use an async function as a middleware [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/middleware/fn.from_fn.html) -- `RedirectHttps`: middleware to redirect traffic to HTTPS if connection is insecure with optional HSTS [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/middleware/struct.RedirectHttps.html) -- `redirect_to_www`: function middleware to redirect traffic to `www.` if not already there [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/middleware/fn.redirect_to_www.html) -- `ErrorHandlers`: alternative error handler middleware with simpler interface [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/middleware/struct.ErrorHandlers.html) +- `from_fn`: use an async function as a middleware [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/middleware/fn.from_fn.html) +- `RedirectHttps`: middleware to redirect traffic to HTTPS if connection is insecure with optional HSTS [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/middleware/struct.RedirectHttps.html) +- `redirect_to_www`: function middleware to redirect traffic to `www.` if not already there [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/middleware/fn.redirect_to_www.html) +- `ErrorHandlers`: alternative error handler middleware with simpler interface [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/middleware/struct.ErrorHandlers.html) ### Extractors -- `LazyData`: app data/state initialized on first use [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.LazyData.html) -- `SwapData`: app data/state that can be replaced at runtime (alternative to `Data>`) [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.SwapData.html) -- `LocalData`: app data/state that uses an `Rc` internally, avoiding atomic overhead (alternative to `Data>`) [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.DataSwap.html) -- `Json`: simplified JSON extractor with const-generic limits [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.Json.html) -- `Path`: simplified path parameter extractor that supports destructuring [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.Path.html) -- `Query`: simplified query-string extractor that can also collect multi-value items [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.Query.html) -- `RequestSignature`: wraps an extractor and calculates a request signature alongside [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/extract/struct.RequestSignature.html) +- `LazyData`: app data/state initialized on first use [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.LazyData.html) +- `SwapData`: app data/state that can be replaced at runtime (alternative to `Data>`) [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.SwapData.html) +- `LocalData`: app data/state that uses an `Rc` internally, avoiding atomic overhead (alternative to `Data>`) [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.DataSwap.html) +- `Json`: simplified JSON extractor with const-generic limits [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.Json.html) +- `Path`: simplified path parameter extractor that supports destructuring [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.Path.html) +- `Query`: simplified query-string extractor that can also collect multi-value items [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.Query.html) +- `RequestSignature`: wraps an extractor and calculates a request signature alongside [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/extract/struct.RequestSignature.html) ### Macros -- `FromRequest`: Derive macro to implement `FromRequest` on an aggregate struct of other extractors [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/derive.FromRequest.html) +- `FromRequest`: Derive macro to implement `FromRequest` on an aggregate struct of other extractors [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/derive.FromRequest.html) ### Headers -- `Hsts`: Strict-Transport-Security (HSTS) [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/header/struct.Hsts.html) -- `CacheControl`: Cache-Control header with support for modern directives [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/header/struct.CacheControl.html) +- `Hsts`: Strict-Transport-Security (HSTS) [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/header/struct.Hsts.html) +- `CacheControl`: Cache-Control header with support for modern directives [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/header/struct.CacheControl.html) ### Body Types -- `channel`: a simple channel-like body type with a sender side that can be used from another thread [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/body/fn.channel.html) +- `channel`: a simple channel-like body type with a sender side that can be used from another thread [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/body/fn.channel.html) ### Services -- `Redirect`: simple redirects [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/web/struct.Redirect.html) -- `spa`: Easy Single-page Application (SPA) service [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/web/fn.spa.html) +- `Redirect`: simple redirects [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/web/struct.Redirect.html) +- `spa`: Easy Single-page Application (SPA) service [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/web/fn.spa.html) ### Route Guards -- `Acceptable`: verifies that an `Accept` header is present and it contains a compatible MIME type [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/guard/struct.Acceptable.html) +- `Acceptable`: verifies that an `Accept` header is present and it contains a compatible MIME type [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/guard/struct.Acceptable.html) ### Test Utilities -- `test_request`: Construct `TestRequest` using an HTTP-like DSL [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/test/macro.assert_response_matches.html) -- `assert_response_matches`: quickly write tests that check various parts of a `ServiceResponse` [(docs)](https://docs.rs/actix-web-lab/0.16.0/actix_web_lab/test/macro.assert_response_matches.html) +- `test_request`: Construct `TestRequest` using an HTTP-like DSL [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/test/macro.assert_response_matches.html) +- `assert_response_matches`: quickly write tests that check various parts of a `ServiceResponse` [(docs)](https://docs.rs/actix-web-lab/0.16.1/actix_web_lab/test/macro.assert_response_matches.html) ## Things To Know About This Crate diff --git a/actix-web-lab/src/query.rs b/actix-web-lab/src/query.rs index 0742e60b..ccceee6e 100644 --- a/actix-web-lab/src/query.rs +++ b/actix-web-lab/src/query.rs @@ -12,55 +12,56 @@ use tracing::debug; /// Extract typed information from the request's query. /// -/// This currently identical in purpose to the `Query` extractor in `actix-web` but it will be able -/// to track version bumps of `serde-urlencoded` more closely. This version also does away with the -/// custom error handler config. -/// /// To extract typed data from the URL query string, the inner type `T` must implement the /// [`DeserializeOwned`] trait. /// +/// # Differences From `actix_web::web::Query` +/// This extractor uses `serde_html_form` under-the-hood which supports multi-value items. These are +/// sent by HTML select inputs when multiple options are chosen and can be collected into a `Vec`. +/// +/// This version also removes the custom error handler config; users should instead prefer to handle +/// errors using the explicit `Result, E>` extractor in their handlers. +/// /// # Panics /// A query string consists of unordered `key=value` pairs, therefore it cannot be decoded into any /// type which depends upon data ordering (eg. tuples). Trying to do so will result in a panic. /// /// # Examples /// ``` -/// use actix_web::get; +/// use actix_web::{get, Responder}; /// use actix_web_lab::extract::Query; /// use serde::Deserialize; /// /// #[derive(Debug, Deserialize)] -/// pub enum ResponseType { -/// Token, -/// Code +/// #[serde(rename_all = "lowercase")] +/// enum LogType { +/// Reports, +/// Actions, /// } /// /// #[derive(Debug, Deserialize)] -/// pub struct AuthRequest { -/// id: u64, -/// response_type: ResponseType, -/// } +/// pub struct LogsParams { +/// #[serde(rename = "type")] +/// log_type: u64, /// -/// // Deserialize `AuthRequest` struct from query string. -/// // This handler gets called only if the request's query parameters contain both fields. -/// // A valid request path for this handler would be `/?id=64&response_type=Code"`. -/// #[get("/")] -/// async fn index(info: Query) -> String { -/// format!("Authorization request for id={} and type={:?}!", info.id, info.response_type) +/// #[serde(rename = "user")] +/// users: Vec, /// } /// -/// // To access the entire underlying query struct, use `.into_inner()`. -/// #[get("/debug1")] -/// async fn debug1(info: Query) -> String { -/// dbg!("Authorization object = {:?}", info.into_inner()); -/// "OK".to_string() +/// // Deserialize `LogsParams` struct from query string. +/// // This handler gets called only if the request's query parameters contain both fields. +/// // A valid request path for this handler would be `/logs?type=reports&user=foo&user=bar"`. +/// #[get("/logs")] +/// async fn index(info: Query) -> impl Responder { +/// let LogsParams { log_type, users } = info.into_inner(); +/// format!("Logs request for type={log_type} and user list={users:?}!") /// } /// /// // Or use destructuring, which is equivalent to `.into_inner()`. /// #[get("/debug2")] -/// async fn debug2(Query(info): Query) -> String { -/// dbg!("Authorization object = {:?}", info); -/// "OK".to_string() +/// async fn debug2(Query(info): Query) -> impl Responder { +/// dbg!("Authorization object = {info:?}"); +/// "OK" /// } /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]