Compare commits

..

7 Commits

Author SHA1 Message Date
Greg Johnston
f7b08cf9cc Merge branch 'main' into server-fn-test-fix 2023-06-13 16:23:46 -04:00
Greg Johnston
7e16d115e3 docs: fix failing doctests for server fns 2023-06-13 16:22:39 -04:00
Greg Johnston
b043f829a6 docs: clarify available server fn encodings (#1178) 2023-06-13 16:01:45 -04:00
Greg Johnston
af3596a608 fmt 2023-06-13 16:01:10 -04:00
Greg Johnston
3c66712f4d remove references to chapters when not in book 2023-06-13 16:00:48 -04:00
Greg Johnston
ec60a0f4fe docs: clarify available server fn encodings 2023-06-13 15:57:28 -04:00
jquesada2016
f415f7b146 fix: removes in new <For/> causing panics in some circumstances (#1173) 2023-06-13 15:43:02 -04:00
6 changed files with 126 additions and 7 deletions

View File

@@ -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 `<form>` support. For better or for worse, HTML forms dont support `PUT` or `DELETE`, and they dont 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 well see [in a later chapter](../progressive_enhancement), this isnt always a great idea.
>
> The CBOR encoding is suported for historical reasons; an earlier version of server functions used a URL encoding that didnt 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 its very important to remember. **Server functions are not magic; theyre syntax sugar for defining a public API.** The _body_ of a server function is never made public; its just part of your server binary. But the server function is a publicly accessible API endpoint, and its return value is just a JSON or similar blob. You should _never_ return something sensitive from a server function.

View File

@@ -1,5 +1,5 @@
use crate::Children;
use leptos_dom::{Errors, HydrationCtx, IntoView};
use leptos_dom::{Errors, IntoView};
use leptos_macro::{component, view};
use leptos_reactive::{
create_rw_signal, provide_context, signal_prelude::*, RwSignal, Scope,
@@ -28,7 +28,7 @@ use leptos_reactive::{
/// }
/// # });
/// ```
#[component]
#[component(transparent)]
pub fn ErrorBoundary<F, IV>(
cx: Scope,
/// The components inside the tag which will get rendered
@@ -40,7 +40,6 @@ where
F: Fn(Scope, RwSignal<Errors>) -> IV + 'static,
IV: IntoView,
{
_ = HydrationCtx::next_component();
let errors: RwSignal<Errors> = create_rw_signal(cx, Errors::default());
provide_context(cx, errors);

View File

@@ -828,7 +828,7 @@ fn apply_diff<T, EF, V>(
}
for DiffOpRemove { at } in &diff.removed {
let item_to_remove = std::mem::take(&mut children[*at]).unwrap();
let item_to_remove = children[*at].take().unwrap();
item_to_remove.prepare_for_move();
}
@@ -910,21 +910,34 @@ fn apply_diff<T, EF, V>(
/// Unpacks adds and moves into a sequence of interleaved
/// add and move commands. Move commands will always return
/// with a `len == 1` and `is_dense = true`.
/// with a `len == 1`.
#[cfg(all(target_arch = "wasm32", feature = "web"))]
fn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {
let mut moves = Vec::with_capacity(diff.items_to_move);
let mut adds = Vec::with_capacity(diff.added.len());
let mut removes_iter = diff.removed.iter();
let mut adds_iter = diff.added.iter();
let mut moves_iter = diff.moved.iter();
let mut removes_next = removes_iter.next();
let mut adds_next = adds_iter.next();
let mut moves_next = moves_iter.next().copied();
for i in 0..diff.items_to_move + diff.added.len() {
let mut from_offset: usize = 0;
for i in 0..diff.items_to_move + diff.added.len() + diff.removed.len() {
match (adds_next, &mut moves_next) {
(Some(add), Some(move_)) => {
if let Some(DiffOpRemove { at }) = removes_next {
if *at == i {
from_offset += 1;
removes_next = removes_iter.next();
continue;
}
}
if add.at == i {
adds.push(*add);
@@ -932,6 +945,7 @@ fn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {
} else {
let mut single_move = *move_;
single_move.len = 1;
single_move.from -= from_offset;
moves.push(single_move);
@@ -950,8 +964,18 @@ fn unpack_moves(diff: &Diff) -> (Vec<DiffOpMove>, Vec<DiffOpAdd>) {
adds_next = adds_iter.next();
}
(None, Some(move_)) => {
if let Some(DiffOpRemove { at }) = removes_next {
if *at == i {
from_offset += 1;
removes_next = removes_iter.next();
continue;
}
}
let mut single_move = *move_;
single_move.len = 1;
single_move.from -= from_offset;
moves.push(single_move);

View File

@@ -80,7 +80,7 @@ where
E: Error + Send + Sync + 'static,
{
fn into_view(self, cx: leptos_reactive::Scope) -> crate::View {
let id = ErrorKey(HydrationCtx::peek().fragment.to_string().into());
let id = ErrorKey(HydrationCtx::peek().id.to_string().into());
let errors = use_context::<RwSignal<Errors>>(cx);
match self {
Ok(stuff) => {

View File

@@ -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,ignore
/// #[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 `<form>` support. For better or for worse,
/// HTML forms dont support `PUT` or `DELETE`, and they dont 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
/// didnt 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 {

View File

@@ -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,ignore
//! #[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 `<form>` support. For better or for worse,
//! HTML forms dont support `PUT` or `DELETE`, and they dont 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
//! didnt 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};