diff --git a/examples/server_fns_axum/src/app.rs b/examples/server_fns_axum/src/app.rs index a6ec1fe8a4..07eb960925 100644 --- a/examples/server_fns_axum/src/app.rs +++ b/examples/server_fns_axum/src/app.rs @@ -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 { + } } @@ -795,3 +798,55 @@ pub fn CustomEncoding() -> impl IntoView {

{result}

} } + +/// 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 Client 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>> + + 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! { +

Custom clients

+

You can define a custom server function client to do something like adding a header to every request.

+

Check the network request in your browser devtools to see how this client adds a custom header.

+ + } +} diff --git a/server_fn/src/request/browser.rs b/server_fn/src/request/browser.rs index 820ea77ffb..41978cc8dc 100644 --- a/server_fn/src/request/browser.rs +++ b/server_fn/src/request/browser.rs @@ -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 for BrowserRequest { } } +impl From for Request { + fn from(value: BrowserRequest) -> Self { + value.0.take() + } +} + +impl From 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);