diff --git a/docs/book/src/server/25_server_functions.md b/docs/book/src/server/25_server_functions.md index 1381e8aca..862c61253 100644 --- a/docs/book/src/server/25_server_functions.md +++ b/docs/book/src/server/25_server_functions.md @@ -97,6 +97,14 @@ In other words, you have two choices: **But remember**: Leptos will handle all the details of this encoding and decoding for you. When you use a server function, it looks just like calling any other asynchronous function! +> **Why not `PUT` or `DELETE`? Why URL/form encoding, and not JSON?** +> +> These are reasonable questions. Much of the web is built on REST API patterns that encourage the use of semantic HTTP methods like `DELETE` to delete an item from a database, and many devs are accustomed to sending data to APIs in the JSON format. +> +> The reason we use `POST` or `GET` with URL-encoded data by default is the `
` support. For better or for worse, HTML forms don’t support `PUT` or `DELETE`, and they don’t support sending JSON. This means that if you use anything but a `GET` or `POST` request with URL-encoded data, it can only work once WASM has loaded. As we’ll see [in a later chapter](../progressive_enhancement), this isn’t always a great idea. +> +> The CBOR encoding is suported for historical reasons; an earlier version of server functions used a URL encoding that didn’t support nested objects like structs or vectors as server function arguments, which CBOR did. But note that the CBOR forms encounter the same issue as `PUT`, `DELETE`, or JSON: they do not degrade gracefully if the WASM version of your app is not available. + ## An Important Note on Security Server functions are a cool technology, but it’s very important to remember. **Server functions are not magic; they’re syntax sugar for defining a public API.** The _body_ of a server function is never made public; it’s just part of your server binary. But the server function is a publicly accessible API endpoint, and it’s return value is just a JSON or similar blob. You should _never_ return something sensitive from a server function. diff --git a/leptos_macro/src/lib.rs b/leptos_macro/src/lib.rs index 1b0bf084e..176badac4 100644 --- a/leptos_macro/src/lib.rs +++ b/leptos_macro/src/lib.rs @@ -840,6 +840,50 @@ pub fn slot(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { /// - **The `Scope` comes from the server.** Optionally, the first argument of a server function /// can be a Leptos `Scope`. This scope can be used to inject dependencies like the HTTP request /// or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client. +/// +/// ## Server Function Encodings +/// +/// By default, the server function call is a `POST` request that serializes the arguments as URL-encoded form data in the body +/// of the request. But there are a few other methods supported. Optionally, we can provide another argument to the `#[server]` +/// macro to specify an alternate encoding: +/// +/// ```rust +/// #[server(AddTodo, "/api", "Url")] +/// #[server(AddTodo, "/api", "GetJson")] +/// #[server(AddTodo, "/api", "Cbor")] +/// #[server(AddTodo, "/api", "GetCbor")] +/// ``` +/// +/// The four options use different combinations of HTTP verbs and encoding methods: +/// +/// | Name | Method | Request | Response | +/// | ----------------- | ------ | ----------- | -------- | +/// | **Url** (default) | POST | URL encoded | JSON | +/// | **GetJson** | GET | URL encoded | JSON | +/// | **Cbor** | POST | CBOR | CBOR | +/// | **GetCbor** | GET | URL encoded | CBOR | +/// +/// In other words, you have two choices: +/// +/// - `GET` or `POST`? This has implications for things like browser or CDN caching; while `POST` requests should not be cached, +/// `GET` requests can be. +/// - Plain text (arguments sent with URL/form encoding, results sent as JSON) or a binary format (CBOR, encoded as a base64 +/// string)? +/// +/// ## Why not `PUT` or `DELETE`? Why URL/form encoding, and not JSON?** +/// +/// These are reasonable questions. Much of the web is built on REST API patterns that encourage the use of semantic HTTP +/// methods like `DELETE` to delete an item from a database, and many devs are accustomed to sending data to APIs in the +/// JSON format. +/// +/// The reason we use `POST` or `GET` with URL-encoded data by default is the `` support. For better or for worse, +/// HTML forms don’t support `PUT` or `DELETE`, and they don’t support sending JSON. This means that if you use anything +/// but a `GET` or `POST` request with URL-encoded data, it can only work once WASM has loaded. +/// +/// The CBOR encoding is suported for historical reasons; an earlier version of server functions used a URL encoding that +/// didn’t support nested objects like structs or vectors as server function arguments, which CBOR did. But note that the +/// CBOR forms encounter the same issue as `PUT`, `DELETE`, or JSON: they do not degrade gracefully if the WASM version of +/// your app is not available. #[proc_macro_attribute] #[proc_macro_error] pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream { diff --git a/leptos_server/src/lib.rs b/leptos_server/src/lib.rs index 66419610c..35962282c 100644 --- a/leptos_server/src/lib.rs +++ b/leptos_server/src/lib.rs @@ -70,6 +70,50 @@ //! - **The [Scope](leptos_reactive::Scope) comes from the server.** Optionally, the first argument of a server function //! can be a Leptos [Scope](leptos_reactive::Scope). This scope can be used to inject dependencies like the HTTP request //! or response or other server-only dependencies, but it does *not* have access to reactive state that exists in the client. +//! +//! ## Server Function Encodings +//! +//! By default, the server function call is a `POST` request that serializes the arguments as URL-encoded form data in the body +//! of the request. But there are a few other methods supported. Optionally, we can provide another argument to the `#[server]` +//! macro to specify an alternate encoding: +//! +//! ```rust +//! #[server(AddTodo, "/api", "Url")] +//! #[server(AddTodo, "/api", "GetJson")] +//! #[server(AddTodo, "/api", "Cbor")] +//! #[server(AddTodo, "/api", "GetCbor")] +//! ``` +//! +//! The four options use different combinations of HTTP verbs and encoding methods: +//! +//! | Name | Method | Request | Response | +//! | ----------------- | ------ | ----------- | -------- | +//! | **Url** (default) | POST | URL encoded | JSON | +//! | **GetJson** | GET | URL encoded | JSON | +//! | **Cbor** | POST | CBOR | CBOR | +//! | **GetCbor** | GET | URL encoded | CBOR | +//! +//! In other words, you have two choices: +//! +//! - `GET` or `POST`? This has implications for things like browser or CDN caching; while `POST` requests should not be cached, +//! `GET` requests can be. +//! - Plain text (arguments sent with URL/form encoding, results sent as JSON) or a binary format (CBOR, encoded as a base64 +//! string)? +//! +//! ## Why not `PUT` or `DELETE`? Why URL/form encoding, and not JSON?** +//! +//! These are reasonable questions. Much of the web is built on REST API patterns that encourage the use of semantic HTTP +//! methods like `DELETE` to delete an item from a database, and many devs are accustomed to sending data to APIs in the +//! JSON format. +//! +//! The reason we use `POST` or `GET` with URL-encoded data by default is the `` support. For better or for worse, +//! HTML forms don’t support `PUT` or `DELETE`, and they don’t support sending JSON. This means that if you use anything +//! but a `GET` or `POST` request with URL-encoded data, it can only work once WASM has loaded. +//! +//! The CBOR encoding is suported for historical reasons; an earlier version of server functions used a URL encoding that +//! didn’t support nested objects like structs or vectors as server function arguments, which CBOR did. But note that the +//! CBOR forms encounter the same issue as `PUT`, `DELETE`, or JSON: they do not degrade gracefully if the WASM version of +//! your app is not available. use leptos_reactive::*; pub use server_fn::{Encoding, Payload, ServerFnError};