More fullstack fixes (#4737)

* allow `--server` only builds!

* rollback example

* capture `/:route` routes

* fix: properly implement file uploads in forms

* implement desktop form uploads

* Performant serialization of form bytes

* finish up file stuff

* cleanups

* add request headers to streaming context

* remove some unwraps
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-10-07 23:07:43 -07:00
1 parent fc01a002fd
commit 92792aded9
47 files changed
+1241 -703

No files matched your search

+7 -4
View File
@@ -52,11 +52,14 @@ fn app() -> Element {
#[post("/api/upload-multipart")]
async fn upload(mut form: MultipartFormData) -> Result<()> {
while let Ok(Some(field)) = form.next_field().await {
let name = field.name().unwrap_or("<none>").to_string();
let file_name = field.file_name().unwrap_or("<none>").to_string();
let content_type = field.content_type().unwrap_or("<none>").to_string();
let size = field.bytes().await.unwrap().len();
info!(
"Field name: {:?}, filename: {:?}, content_type: {:?}",
field.name().to_owned(),
field.file_name(),
field.content_type(),
"Field name: {:?}, filename: {:?}, content_type: {:?}, size: {:?}",
name, file_name, content_type, size
);
}
+1
View File
@@ -17,6 +17,7 @@ Option 1:
For simple, synchronous, thread-safe data, we can use statics with atomic types or mutexes.
The `LazyLock` type from the standard library is a great choice for simple, synchronous data
*/
#[cfg(feature = "server")]
static MESSAGES: LazyLock<Mutex<Vec<String>>> = LazyLock::new(|| Mutex::new(Vec::new()));
#[post("/api/messages")]
+6 -4
View File
@@ -4,15 +4,14 @@
//! in the "values" field.
use dioxus::prelude::*;
use std::collections::HashMap;
fn main() {
dioxus::launch(app);
}
fn app() -> Element {
let mut values = use_signal(HashMap::new);
let mut submitted_values = use_signal(HashMap::new);
let mut values = use_signal(Vec::new);
let mut submitted_values = use_signal(Vec::new);
rsx! {
div { style: "display: flex",
@@ -63,7 +62,7 @@ fn app() -> Element {
input { r#type: "text", name: "full-name" }
label { r#for: "email", "Email (matching <name>@example.com)" }
input { r#type: "email", pattern: ".+@example\\.com", size: "30", required: "true", id: "email", name: "email" }
input { r#type: "email", size: "30", id: "email", name: "email" }
label { r#for: "password", "Password" }
input { r#type: "password", name: "password" }
@@ -122,6 +121,9 @@ fn app() -> Element {
input { r#type: "checkbox", name: "cbox", value: "yellow", id: "cbox-yellow" }
}
// File input
label { r#for: "headshot", "Headshot" }
input { r#type: "file", name: "headshot", id: "headshot", multiple: true, accept: ".png,.jpg,.jpeg" }
// Buttons will submit your form by default.
button { r#type: "submit", value: "Submit", "Submit the form" }