mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 12:31:55 -05:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1bc734dcf | ||
|
|
f71b4aae69 | ||
|
|
a834c03974 | ||
|
|
595013579c |
26
Cargo.toml
26
Cargo.toml
@@ -25,22 +25,22 @@ members = [
|
||||
exclude = ["benchmarks", "examples"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
|
||||
[workspace.dependencies]
|
||||
leptos = { path = "./leptos", version = "0.6.3" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.6.3" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.6.3" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.6.3" }
|
||||
leptos_reactive = { path = "./leptos_reactive", version = "0.6.3" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.6.3" }
|
||||
server_fn = { path = "./server_fn", version = "0.6.3" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.6.3" }
|
||||
leptos = { path = "./leptos", version = "0.6.5" }
|
||||
leptos_dom = { path = "./leptos_dom", version = "0.6.5" }
|
||||
leptos_hot_reload = { path = "./leptos_hot_reload", version = "0.6.5" }
|
||||
leptos_macro = { path = "./leptos_macro", version = "0.6.5" }
|
||||
leptos_reactive = { path = "./leptos_reactive", version = "0.6.5" }
|
||||
leptos_server = { path = "./leptos_server", version = "0.6.5" }
|
||||
server_fn = { path = "./server_fn", version = "0.6.5" }
|
||||
server_fn_macro = { path = "./server_fn_macro", version = "0.6.5" }
|
||||
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.6" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.6.3" }
|
||||
leptos_router = { path = "./router", version = "0.6.3" }
|
||||
leptos_meta = { path = "./meta", version = "0.6.3" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.6.3" }
|
||||
leptos_config = { path = "./leptos_config", version = "0.6.5" }
|
||||
leptos_router = { path = "./router", version = "0.6.5" }
|
||||
leptos_meta = { path = "./meta", version = "0.6.5" }
|
||||
leptos_integration_utils = { path = "./integrations/utils", version = "0.6.5" }
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
@@ -5,13 +5,15 @@ use leptos_meta::{provide_meta_context, Link, Meta, Stylesheet};
|
||||
use leptos_router::{ActionForm, Route, Router, Routes};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use server_fn::{
|
||||
client::{browser::BrowserClient, Client},
|
||||
codec::{
|
||||
Encoding, FromReq, FromRes, GetUrl, IntoReq, IntoRes, MultipartData,
|
||||
MultipartFormData, Rkyv, SerdeLite, StreamingText, TextStream,
|
||||
},
|
||||
request::{ClientReq, Req},
|
||||
response::{ClientRes, Res},
|
||||
request::{browser::BrowserRequest, ClientReq, Req},
|
||||
response::{browser::BrowserResponse, ClientRes, Res},
|
||||
};
|
||||
use std::future::Future;
|
||||
#[cfg(feature = "ssr")]
|
||||
use std::sync::{
|
||||
atomic::{AtomicU8, Ordering},
|
||||
@@ -58,6 +60,7 @@ pub fn HomePage() -> impl IntoView {
|
||||
<FileUploadWithProgress/>
|
||||
<FileWatcher/>
|
||||
<CustomEncoding/>
|
||||
<CustomClientExample/>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,3 +798,55 @@ pub fn CustomEncoding() -> impl IntoView {
|
||||
<p>{result}</p>
|
||||
}
|
||||
}
|
||||
|
||||
/// Middleware lets you modify the request/response on the server.
|
||||
///
|
||||
/// On the client, you might also want to modify the request. For example, you may need to add a
|
||||
/// custom header for authentication on every request. You can do this by creating a "custom
|
||||
/// client."
|
||||
#[component]
|
||||
pub fn CustomClientExample() -> impl IntoView {
|
||||
// Define a type for our client.
|
||||
pub struct CustomClient;
|
||||
|
||||
// Implement the `Client` trait for it.
|
||||
impl<CustErr> Client<CustErr> for CustomClient {
|
||||
// BrowserRequest and BrowserResponse are the defaults used by other server functions.
|
||||
// They are wrappers for the underlying Web Fetch API types.
|
||||
type Request = BrowserRequest;
|
||||
type Response = BrowserResponse;
|
||||
|
||||
// Our custom `send()` implementation does all the work.
|
||||
fn send(
|
||||
req: Self::Request,
|
||||
) -> impl Future<Output = Result<Self::Response, ServerFnError<CustErr>>>
|
||||
+ Send {
|
||||
// BrowserRequest derefs to the underlying Request type from gloo-net,
|
||||
// so we can get access to the headers here
|
||||
let headers = req.headers();
|
||||
// modify the headers by appending one
|
||||
headers.append("X-Custom-Header", "foobar");
|
||||
// delegate back out to BrowserClient to send the modified request
|
||||
BrowserClient::send(req)
|
||||
}
|
||||
}
|
||||
|
||||
// Specify our custom client with `client = `
|
||||
#[server(client = CustomClient)]
|
||||
pub async fn fn_with_custom_client() -> Result<(), ServerFnError> {
|
||||
use http::header::HeaderMap;
|
||||
use leptos_axum::extract;
|
||||
|
||||
let headers: HeaderMap = extract().await?;
|
||||
let custom_header = headers.get("X-Custom-Header");
|
||||
println!("X-Custom-Header = {custom_header:?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
view! {
|
||||
<h3>Custom clients</h3>
|
||||
<p>You can define a custom server function client to do something like adding a header to every request.</p>
|
||||
<p>Check the network request in your browser devtools to see how this client adds a custom header.</p>
|
||||
<button on:click=|_| spawn_local(async { fn_with_custom_client().await.unwrap() })>Click me</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,14 +150,6 @@ pub fn redirect(path: &str) {
|
||||
to redirect()."
|
||||
);
|
||||
}
|
||||
if let Some(response_options) = use_context::<ResponseOptions>() {
|
||||
response_options.set_status(StatusCode::FOUND);
|
||||
response_options.insert_header(
|
||||
header::LOCATION,
|
||||
header::HeaderValue::from_str(path)
|
||||
.expect("Failed to create HeaderValue"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// An Actix [struct@Route](actix_web::Route) that listens for a `POST` request with
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leptos_meta"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leptos_router"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```rust
|
||||
//!
|
||||
//!
|
||||
//! use leptos::*;
|
||||
//! use leptos_router::*;
|
||||
//!
|
||||
|
||||
@@ -40,7 +40,7 @@ where
|
||||
{
|
||||
async fn from_req(req: Request) -> Result<Self, ServerFnError<CustErr>> {
|
||||
let string_data = req.as_query().unwrap_or_default();
|
||||
let args = serde_qs::from_str::<Self>(&string_data)
|
||||
let args = serde_qs::from_str::<Self>(string_data)
|
||||
.map_err(|e| ServerFnError::Args(e.to_string()))?;
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
@@ -441,7 +441,6 @@ impl<Req, Res> Clone for ServerFnTraitObj<Req, Res> {
|
||||
}
|
||||
|
||||
#[allow(unused)] // used by server integrations
|
||||
/// A neat little type that stores our trait representations of your server functions
|
||||
type LazyServerFnMap<Req, Res> =
|
||||
Lazy<DashMap<&'static str, ServerFnTraitObj<Req, Res>>>;
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ impl<CustErr> Req<CustErr> for ActixRequest
|
||||
where
|
||||
CustErr: 'static,
|
||||
{
|
||||
fn as_query(&self) -> Option<Cow<'_, str>> {
|
||||
self.0 .0.uri().query().map(|q| q.into())
|
||||
fn as_query(&self) -> Option<&str> {
|
||||
self.0 .0.uri().query()
|
||||
}
|
||||
|
||||
fn to_content_type(&self) -> Option<Cow<'_, str>> {
|
||||
|
||||
@@ -12,8 +12,8 @@ impl<CustErr> Req<CustErr> for Request<Body>
|
||||
where
|
||||
CustErr: 'static,
|
||||
{
|
||||
fn as_query(&self) -> Option<Cow<'_, str>> {
|
||||
self.uri().query().map(|q| q.into())
|
||||
fn as_query(&self) -> Option<&str> {
|
||||
self.uri().query()
|
||||
}
|
||||
|
||||
fn to_content_type(&self) -> Option<Cow<'_, str>> {
|
||||
|
||||
@@ -5,6 +5,7 @@ use futures::{Stream, StreamExt};
|
||||
pub use gloo_net::http::Request;
|
||||
use js_sys::{Reflect, Uint8Array};
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_streams::ReadableStream;
|
||||
use web_sys::{FormData, Headers, RequestInit, UrlSearchParams};
|
||||
@@ -19,6 +20,32 @@ impl From<Request> for BrowserRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BrowserRequest> for Request {
|
||||
fn from(value: BrowserRequest) -> Self {
|
||||
value.0.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BrowserRequest> for web_sys::Request {
|
||||
fn from(value: BrowserRequest) -> Self {
|
||||
value.0.take().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BrowserRequest {
|
||||
type Target = Request;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for BrowserRequest {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.0.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// The `FormData` type available in the browser.
|
||||
#[derive(Debug)]
|
||||
pub struct BrowserFormData(pub(crate) SendWrapper<FormData>);
|
||||
|
||||
@@ -78,7 +78,7 @@ where
|
||||
Self: Sized,
|
||||
{
|
||||
/// Returns the query string of the request’s URL, starting after the `?`.
|
||||
fn as_query(&self) -> Option<Cow<'_, str>>;
|
||||
fn as_query(&self) -> Option<&str>;
|
||||
|
||||
/// Returns the `Content-Type` header, if any.
|
||||
fn to_content_type(&self) -> Option<Cow<'_, str>>;
|
||||
@@ -116,7 +116,7 @@ impl<CustErr> Req<CustErr> for BrowserMockReq
|
||||
where
|
||||
CustErr: 'static,
|
||||
{
|
||||
fn as_query(&self) -> Option<Cow<'_, str>> {
|
||||
fn as_query(&self) -> Option<&str> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,15 @@ pub fn server_macro_impl(
|
||||
.inputs
|
||||
.iter_mut()
|
||||
.map(|f| {
|
||||
let typed_arg =
|
||||
match f {
|
||||
FnArg::Receiver(_) => return Err(syn::Error::new(
|
||||
let typed_arg = match f {
|
||||
FnArg::Receiver(_) => {
|
||||
return Err(syn::Error::new(
|
||||
f.span(),
|
||||
"cannot use receiver types in server function macro",
|
||||
)),
|
||||
FnArg::Typed(t) => t,
|
||||
};
|
||||
))
|
||||
}
|
||||
FnArg::Typed(t) => t,
|
||||
};
|
||||
|
||||
// strip `mut`, which is allowed in fn args but not in struct fields
|
||||
if let Pat::Ident(ident) = &mut *typed_arg.pat {
|
||||
|
||||
Reference in New Issue
Block a user