mirror of
https://github.com/tokio-rs/axum.git
synced 2024-11-26 00:56:27 +01:00
Replace public use of mime crate with &str (#642)
Replaces `Field::content_type`'s return type with `&str`. This is a breaking change. Closes #637
This commit is contained in:
parent
4df4e07e10
commit
9b4232b786
4 changed files with 58 additions and 8 deletions
|
@ -53,7 +53,7 @@ tokio-tungstenite = { optional = true, version = "0.16" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
|
reqwest = { version = "0.11", default-features = false, features = ["json", "stream", "multipart"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tokio = { version = "1.6.1", features = ["macros", "rt", "rt-multi-thread", "net", "test-util"] }
|
tokio = { version = "1.6.1", features = ["macros", "rt", "rt-multi-thread", "net", "test-util"] }
|
||||||
|
|
|
@ -8,14 +8,13 @@ use crate::BoxError;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures_util::stream::Stream;
|
use futures_util::stream::Stream;
|
||||||
use http::header::{HeaderMap, CONTENT_TYPE};
|
use http::header::{HeaderMap, CONTENT_TYPE};
|
||||||
use mime::Mime;
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extractor that parses `multipart/form-data` requests commonly used with file uploads.
|
/// Extractor that parses `multipart/form-data` requests (commonly used with file uploads).
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -42,7 +41,7 @@ use std::{
|
||||||
/// # };
|
/// # };
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// For security reasons its recommended to combine this with
|
/// For security reasons it's recommended to combine this with
|
||||||
/// [`ContentLengthLimit`](super::ContentLengthLimit) to limit the size of the request payload.
|
/// [`ContentLengthLimit`](super::ContentLengthLimit) to limit the size of the request payload.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Multipart {
|
pub struct Multipart {
|
||||||
|
@ -120,9 +119,9 @@ impl<'a> Field<'a> {
|
||||||
self.inner.file_name()
|
self.inner.file_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the content type of the field.
|
/// Get the [content type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) of the field.
|
||||||
pub fn content_type(&self) -> Option<&Mime> {
|
pub fn content_type(&self) -> Option<&str> {
|
||||||
self.inner.content_type()
|
self.inner.content_type().map(|m| m.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a map of headers as [`HeaderMap`].
|
/// Get a map of headers as [`HeaderMap`].
|
||||||
|
@ -191,3 +190,40 @@ define_rejection! {
|
||||||
/// missing or invalid.
|
/// missing or invalid.
|
||||||
pub struct InvalidBoundary;
|
pub struct InvalidBoundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{response::IntoResponse, routing::post, test_helpers::*, Router};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn content_type_with_encoding() {
|
||||||
|
const BYTES: &[u8] = "<!doctype html><title>🦀</title>".as_bytes();
|
||||||
|
const FILE_NAME: &str = "index.html";
|
||||||
|
const CONTENT_TYPE: &str = "text/html; charset=utf-8";
|
||||||
|
|
||||||
|
async fn handle(mut multipart: Multipart) -> impl IntoResponse {
|
||||||
|
let field = multipart.next_field().await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(field.file_name().unwrap(), FILE_NAME);
|
||||||
|
assert_eq!(field.content_type().unwrap(), CONTENT_TYPE);
|
||||||
|
assert_eq!(field.bytes().await.unwrap(), BYTES);
|
||||||
|
|
||||||
|
assert!(multipart.next_field().await.unwrap().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = Router::new().route("/", post(handle));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
|
||||||
|
let form = reqwest::multipart::Form::new().part(
|
||||||
|
"file",
|
||||||
|
reqwest::multipart::Part::bytes(BYTES)
|
||||||
|
.file_name(FILE_NAME)
|
||||||
|
.mime_str(CONTENT_TYPE)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
client.post("/").multipart(form).send().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -103,6 +103,7 @@ impl RequestBuilder {
|
||||||
self.builder = self.builder.json(json);
|
self.builder = self.builder.json(json);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn header<K, V>(mut self, key: K, value: V) -> Self
|
pub(crate) fn header<K, V>(mut self, key: K, value: V) -> Self
|
||||||
where
|
where
|
||||||
HeaderName: TryFrom<K>,
|
HeaderName: TryFrom<K>,
|
||||||
|
@ -113,6 +114,11 @@ impl RequestBuilder {
|
||||||
self.builder = self.builder.header(key, value);
|
self.builder = self.builder.header(key, value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn multipart(mut self, form: reqwest::multipart::Form) -> Self {
|
||||||
|
self.builder = self.builder.multipart(form);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TestResponse {
|
pub(crate) struct TestResponse {
|
||||||
|
|
|
@ -65,8 +65,16 @@ async fn accept_form(
|
||||||
) {
|
) {
|
||||||
while let Some(field) = multipart.next_field().await.unwrap() {
|
while let Some(field) = multipart.next_field().await.unwrap() {
|
||||||
let name = field.name().unwrap().to_string();
|
let name = field.name().unwrap().to_string();
|
||||||
|
let file_name = field.file_name().unwrap().to_string();
|
||||||
|
let content_type = field.content_type().unwrap().to_string();
|
||||||
let data = field.bytes().await.unwrap();
|
let data = field.bytes().await.unwrap();
|
||||||
|
|
||||||
println!("Length of `{}` is {} bytes", name, data.len());
|
println!(
|
||||||
|
"Length of `{}` (`{}`: `{}`) is {} bytes",
|
||||||
|
name,
|
||||||
|
file_name,
|
||||||
|
content_type,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue