1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-11-21 17:11:08 +00:00

docs(multipart): improve crate root docs

This commit is contained in:
Rob Ede 2024-07-07 20:32:56 +01:00
parent 16125bd3be
commit e97e28db4f
No known key found for this signature in database
GPG key ID: 97C636207D3EF933
6 changed files with 131 additions and 106 deletions

View file

@ -4,13 +4,14 @@ version = "0.7.2"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Jacob Halsey <jacob@jhalsey.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "Multipart form support for Actix Web"
keywords = ["http", "web", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-web"
license = "MIT OR Apache-2.0"
edition = "2021"
description = "Multipart request & form support for Actix Web"
keywords = ["http", "actix", "web", "multipart", "form"]
homepage.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]

View file

@ -15,14 +15,18 @@
<!-- cargo-rdme start -->
Multipart form support for Actix Web.
Multipart request & form support for Actix Web.
The [`Multipart`] extractor aims to support all kinds of `multipart/*` requests, including `multipart/form-data`, `multipart/related` and `multipart/mixed`. This is a lower-level extractor which supports reading [multipart fields](Field), in the order they are sent by the client.
Due to additional requirements for `multipart/form-data` requests, the higher level [`MultipartForm`] extractor and derive macro only supports this media type.
## Examples
```rust
use actix_web::{post, App, HttpServer, Responder};
use actix_multipart::form::{json::Json as MPJson, tempfile::TempFile, MultipartForm};
use actix_multipart::form::{json::Json as MpJson, tempfile::TempFile, MultipartForm};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
@ -34,7 +38,7 @@ struct Metadata {
struct UploadForm {
#[multipart(limit = "100MB")]
file: TempFile,
json: MPJson<Metadata>,
json: MpJson<Metadata>,
}
#[post("/videos")]
@ -63,6 +67,8 @@ curl -v --request POST \
-F file=@./Cargo.lock
```
[`MultipartForm`]: struct@form::MultipartForm
<!-- cargo-rdme end -->
[More available in the examples repo &rarr;](https://github.com/actix/examples/tree/master/forms/multipart)

View file

@ -1,4 +1,4 @@
//! Process and extract typed data from a multipart stream.
//! Extract and process typed data from fields of a `multipart/form-data` request.
use std::{
any::Any,

View file

@ -1,4 +1,12 @@
//! Multipart form support for Actix Web.
//! Multipart request & form support for Actix Web.
//!
//! The [`Multipart`] extractor aims to support all kinds of `multipart/*` requests, including
//! `multipart/form-data`, `multipart/related` and `multipart/mixed`. This is a lower-level
//! extractor which supports reading [multipart fields](Field), in the order they are sent by the
//! client.
//!
//! Due to additional requirements for `multipart/form-data` requests, the higher level
//! [`MultipartForm`] extractor and derive macro only supports this media type.
//!
//! # Examples
//!
@ -45,6 +53,8 @@
//! -F 'json={"name": "Cargo.lock"};type=application/json' \
//! -F file=@./Cargo.lock
//! ```
//!
//! [`MultipartForm`]: struct@form::MultipartForm
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]

View file

@ -483,7 +483,7 @@ mod tests {
};
use assert_matches::assert_matches;
use futures_test::stream::StreamTestExt as _;
use futures_util::{future::lazy, stream, StreamExt as _};
use futures_util::{stream, StreamExt as _};
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
@ -718,100 +718,6 @@ mod tests {
}
}
#[actix_rt::test]
async fn test_basic() {
let (_, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(payload.buf.len(), 0);
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(None, payload.read_max(1).unwrap());
}
#[actix_rt::test]
async fn test_eof() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_max(4).unwrap());
sender.feed_data(Bytes::from("data"));
sender.feed_eof();
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(Some(Bytes::from("data")), payload.read_max(4).unwrap());
assert_eq!(payload.buf.len(), 0);
assert!(payload.read_max(1).is_err());
assert!(payload.eof);
}
#[actix_rt::test]
async fn test_err() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_max(1).unwrap());
sender.set_error(PayloadError::Incomplete(None));
lazy(|cx| payload.poll_stream(cx)).await.err().unwrap();
}
#[actix_rt::test]
async fn read_max() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(payload.buf.len(), 10);
assert_eq!(Some(Bytes::from("line1")), payload.read_max(5).unwrap());
assert_eq!(payload.buf.len(), 5);
assert_eq!(Some(Bytes::from("line2")), payload.read_max(5).unwrap());
assert_eq!(payload.buf.len(), 0);
}
#[actix_rt::test]
async fn read_exactly() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_exact(2));
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(Some(Bytes::from_static(b"li")), payload.read_exact(2));
assert_eq!(payload.buf.len(), 8);
assert_eq!(Some(Bytes::from_static(b"ne1l")), payload.read_exact(4));
assert_eq!(payload.buf.len(), 4);
}
#[actix_rt::test]
async fn read_until() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_until(b"ne").unwrap());
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(
Some(Bytes::from("line")),
payload.read_until(b"ne").unwrap()
);
assert_eq!(payload.buf.len(), 6);
assert_eq!(
Some(Bytes::from("1line2")),
payload.read_until(b"2").unwrap()
);
assert_eq!(payload.buf.len(), 0);
}
#[actix_rt::test]
async fn test_multipart_from_error() {
let err = Error::ContentTypeMissing;

View file

@ -151,3 +151,105 @@ impl PayloadBuffer {
self.buf.extend_from_slice(&buf);
}
}
#[cfg(test)]
mod tests {
use actix_http::h1;
use futures_util::future::lazy;
use super::*;
#[actix_rt::test]
async fn basic() {
let (_, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(payload.buf.len(), 0);
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(None, payload.read_max(1).unwrap());
}
#[actix_rt::test]
async fn eof() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_max(4).unwrap());
sender.feed_data(Bytes::from("data"));
sender.feed_eof();
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(Some(Bytes::from("data")), payload.read_max(4).unwrap());
assert_eq!(payload.buf.len(), 0);
assert!(payload.read_max(1).is_err());
assert!(payload.eof);
}
#[actix_rt::test]
async fn err() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_max(1).unwrap());
sender.set_error(PayloadError::Incomplete(None));
lazy(|cx| payload.poll_stream(cx)).await.err().unwrap();
}
#[actix_rt::test]
async fn read_max() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(payload.buf.len(), 10);
assert_eq!(Some(Bytes::from("line1")), payload.read_max(5).unwrap());
assert_eq!(payload.buf.len(), 5);
assert_eq!(Some(Bytes::from("line2")), payload.read_max(5).unwrap());
assert_eq!(payload.buf.len(), 0);
}
#[actix_rt::test]
async fn read_exactly() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_exact(2));
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(Some(Bytes::from_static(b"li")), payload.read_exact(2));
assert_eq!(payload.buf.len(), 8);
assert_eq!(Some(Bytes::from_static(b"ne1l")), payload.read_exact(4));
assert_eq!(payload.buf.len(), 4);
}
#[actix_rt::test]
async fn read_until() {
let (mut sender, payload) = h1::Payload::create(false);
let mut payload = PayloadBuffer::new(payload);
assert_eq!(None, payload.read_until(b"ne").unwrap());
sender.feed_data(Bytes::from("line1"));
sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!(
Some(Bytes::from("line")),
payload.read_until(b"ne").unwrap()
);
assert_eq!(payload.buf.len(), 6);
assert_eq!(
Some(Bytes::from("1line2")),
payload.read_until(b"2").unwrap()
);
assert_eq!(payload.buf.len(), 0);
}
}