Skip to content

Commit

Permalink
Add multipart examples for axum and actix-web
Browse files Browse the repository at this point in the history
  • Loading branch information
juhaku committed Dec 11, 2024
1 parent 2a88046 commit 2f7acf1
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 0 deletions.
13 changes: 13 additions & 0 deletions examples/actix-web-multipart/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "actix-web-multipart"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4"
actix-multipart = "0.7"
utoipa = { version = "5", features = ["actix_extras"] }
utoipa-swagger-ui = { version = "8", features = ["actix-web"] }
utoipa-actix-web = "0.1"

[workspace]
8 changes: 8 additions & 0 deletions examples/actix-web-multipart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# actix-web-multipart

This example demonstrates actix-web with utoipa multipart file upload. Browse to `http://localhost:8080/swagger-ui/` to upload a file.

Run with:
```bash
cargo run
```
52 changes: 52 additions & 0 deletions examples/actix-web-multipart/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::io;

use actix_multipart::form::tempfile::TempFile;
use actix_multipart::form::text::Text;
use actix_multipart::form::MultipartForm;
use actix_web::{post, App, HttpServer, Responder};
use utoipa::ToSchema;
use utoipa_actix_web::AppExt;
use utoipa_swagger_ui::SwaggerUi;

#[actix_web::main]
async fn main() -> io::Result<()> {
HttpServer::new(|| {
App::new()
.into_utoipa_app()
.service(hello_form)
.openapi_service(|api| {
SwaggerUi::new("/swagger-ui/{_:.*}").url("/api/openapi.json", api)
})
.into_app()
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

#[derive(ToSchema, MultipartForm)]
struct HelloForm {
#[multipart(limit = "10mb")]
#[schema(value_type = String, format = Binary, content_media_type = "application/octet-stream")]
file: TempFile,
#[schema(value_type = String)]
name: Text<String>,
}

#[utoipa::path(
request_body(content = HelloForm, content_type = "multipart/form-data")
)]
#[post("/hello")]
async fn hello_form(MultipartForm(form): MultipartForm<HelloForm>) -> impl Responder {
let name = form.name.to_string();
let file = &form.file;
format!(
"Greetings: name: {name}, type: {} size: {} file_name: {}!",
file.content_type
.as_ref()
.map(|mime| mime.to_string())
.unwrap_or_default(),
file.size,
file.file_name.as_ref().unwrap_or(&String::new())
)
}
16 changes: 16 additions & 0 deletions examples/axum-multipart/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "axum-multipart"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = { version = "0.7", features = ["multipart"] }
tokio = { version = "1", features = ["full"] }
tower = "0.5"
utoipa = { version = "5", features = ["axum_extras"] }
utoipa-swagger-ui = { version = "8", features = ["axum"] }
utoipa-axum = "0.1"
serde = { features = ["derive"], version = "1" }
serde_json = "1"

[workspace]
8 changes: 8 additions & 0 deletions examples/axum-multipart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# axum-multipart

This example demonstrates axum with utoipa multipart file upload. Browse to `http://localhost:8080/swagger-ui` to upload a file.

Run with:
```bash
cargo run
```
65 changes: 65 additions & 0 deletions examples/axum-multipart/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use axum::extract::Multipart;
use serde::Deserialize;
use utoipa::ToSchema;
use utoipa_axum::router::OpenApiRouter;
use utoipa_axum::routes;
use utoipa_swagger_ui::SwaggerUi;

#[tokio::main]
async fn main() -> std::io::Result<()> {
let (router, api) = OpenApiRouter::new()
.routes(routes!(hello_form))
.split_for_parts();

let router = router.merge(SwaggerUi::new("/swagger-ui").url("/api/openapi.json", api));

let app = router.into_make_service();
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
axum::serve(listener, app).await
}

/// Just a schema for axum native multipart
#[derive(Deserialize, ToSchema)]
#[allow(unused)]
struct HelloForm {
name: String,
#[schema(format = Binary, content_media_type = "application/octet-stream")]
file: String,
}

#[utoipa::path(
post,
path = "/hello",
request_body(content = HelloForm, content_type = "multipart/form-data")
)]
async fn hello_form(mut multipart: Multipart) -> String {
let mut name: Option<String> = None;

let mut content_type: Option<String> = None;
let mut size: usize = 0;
let mut file_name: Option<String> = None;

while let Some(field) = multipart.next_field().await.unwrap() {
let field_name = field.name();

match &field_name {
Some("name") => {
name = Some(field.text().await.expect("should be text for name field"));
}
Some("file") => {
file_name = field.file_name().map(ToString::to_string);
content_type = field.content_type().map(ToString::to_string);
let bytes = field.bytes().await.expect("should be bytes for file field");
size = bytes.len();
}
_ => (),
};
}
format!(
"name: {}, content_type: {}, size: {}, file_name: {}",
name.unwrap_or_default(),
content_type.unwrap_or_default(),
size,
file_name.unwrap_or_default()
)
}

0 comments on commit 2f7acf1

Please sign in to comment.