DioxusLabs / dioxus

Fullstack app framework for web, desktop, mobile, and more.
https://dioxuslabs.com
Apache License 2.0
21.61k stars 830 forks source link

Allow returning HTTP STATUS in fullstack #2739

Open chungwong opened 3 months ago

chungwong commented 3 months ago

Problem

Discord reference Steps To Reproduce Given a route

#[derive(Clone, Routable, PartialEq, Serialize, Deserialize)]
enum Route {
  #[route("/product/:url_slug")]
  Product { url_slug: String },
}

and a component Product where it takes a url and fetch a product by THAT url. It is normal for that to failed due to unrecognised urls. In those case, it would be HTTP 404 NOT FOUND.

#[component]
fn Product(url_slug: String) -> Element {
    let product = use_resource(|| async {
        get_product().await
    });

    rsx! { }
}

The advised solution is to try server_context, however, that doesn't seem to work.

#[server]
async fn get_product() -> Result<String, ServerFnError> {
    if let Ok(mut resp) = server_context().response_parts_mut() {
        resp.status = StatusCode::NOT_FOUND;
        Err(ServerFnError::Response("404 NOT FOUND".into()))
    } else {
        Ok(
            "A dummy product".to_string()
        )
    }
}

Expected behavior The first request in the below screenshot should return 404(or whatever that was returned from the function. For the second AJAX call, that should behave the same?

Screenshots

Screenshot from 2024-07-29 10-28-05

Environment:

ealmloff commented 3 months ago

The fact that setting the status happens after some async work makes this complicated. Fullstack now supports streaming which means there are a few different stages of the response:

Before any content is sent

Fullstack needs to choose chat response is sent to the client. Once the status is sent, it cannot be changed (mostly - we can send an incomplete streaming chunk to fail even after we send a 200 response)

Before the body is started

Fullstack needs to insert any <head> content that should be available for SEO. <head> elements can be streamed in after this, but they may not be visible for SEO

After the body is started

Fullstack can only modify the head and body through javascript. Any text content should still be visible for SEO, but it may not be in the right order

How fullstack should handle this

I was talking with Jon about this, and we came up with this order: 1) Initially fullstack waits and accumulates contents without sending any response 2) Once the suspense boundary that contains the Router renders, fullstack sets the status and renders the head 3) Each new suspense boundary is streamed in like normal