This commit is contained in:
imbolc 2024-06-16 12:43:22 +06:00
parent 13fb55de9a
commit 39e6f9028b
3 changed files with 49 additions and 16 deletions

View file

@ -23,6 +23,7 @@
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Extractors](#extractors) - [Extractors](#extractors)
- [Responders](#responders) - [Responders](#responders)
- [Auto Caching Management](#auto-caching-management)
- [Request Guards](#request-guards) - [Request Guards](#request-guards)
- [Examples](#examples) - [Examples](#examples)
- [Example: Extractors](#example-extractors) - [Example: Extractors](#example-extractors)
@ -76,6 +77,8 @@ any of your responses.
| `HX-Trigger-After-Settle` | `HxResponseTrigger` | `axum_htmx::serde::HxEvent` | | `HX-Trigger-After-Settle` | `HxResponseTrigger` | `axum_htmx::serde::HxEvent` |
| `HX-Trigger-After-Swap` | `HxResponseTrigger` | `axum_htmx::serde::HxEvent` | | `HX-Trigger-After-Swap` | `HxResponseTrigger` | `axum_htmx::serde::HxEvent` |
### Vary Responders
Also, there are corresponding cache-related headers, which you may want to add to Also, there are corresponding cache-related headers, which you may want to add to
`GET` responses, depending on the htmx headers. `GET` responses, depending on the htmx headers.
@ -85,7 +88,7 @@ you need to add `Vary: HX-Request`. That causes the cache to be keyed based on a
composite of the response URL and the `HX-Request` request header - rather than composite of the response URL and the `HX-Request` request header - rather than
being based just on the response URL._ being based just on the response URL._
Refer to [caching htmx docs section](https://htmx.org/docs/#caching) for details. Refer to [caching htmx docs section][htmx-caching] for details.
| Header | Responder | | Header | Responder |
|-------------------------|---------------------| |-------------------------|---------------------|
@ -94,10 +97,27 @@ Refer to [caching htmx docs section](https://htmx.org/docs/#caching) for details
| `Vary: HX-Trigger` | `VaryHxTrigger` | | `Vary: HX-Trigger` | `VaryHxTrigger` |
| `Vary: HX-Trigger-Name` | `VaryHxTriggerName` | | `Vary: HX-Trigger-Name` | `VaryHxTriggerName` |
Look at the [Auto Caching Management](#auto-caching-management) section for
automatic `Vary` headers management.
## Auto Caching Management
__Requires feature `auto-vary`.__
Manual use of [Vary Reponders](#vary-responders) adds fragility to the code,
because of the need to manually control correspondence between used extractors
and the responders.
We provide a [middleware](crate::AutoVaryLayer) to address this issue by
automatically adding `Vary` headers when corresponding extractors are used.
For example, on extracting [`HxRequest`], the middleware automatically adds
`Vary: hx-request` header to the response.
Look at the usage [example][auto-vary-example].
## Request Guards ## Request Guards
__Requires features `guards`.__ __Requires feature `guards`.__
In addition to the extractors, there is also a route-wide layer request guard In addition to the extractors, there is also a route-wide layer request guard
for the `HX-Request` header. This will redirect any requests without the header for the `HX-Request` header. This will redirect any requests without the header
@ -208,7 +228,8 @@ fn router_two() -> Router {
<!-- markdownlint-disable --> <!-- markdownlint-disable -->
| Flag | Default | Description | Dependencies | | Flag | Default | Description | Dependencies |
|----------|----------|------------------------------------------------------------|---------------------------------------------| |-------------|----------|------------------------------------------------------------|---------------------------------------------|
| `auto-vary` | Disabled | A middleware to address [HTMx caching issue][htmx-caching] | `futures`, `tokio`, `tower` |
| `guards` | Disabled | Adds request guard layers. | `tower`, `futures-core`, `pin-project-lite` | | `guards` | Disabled | Adds request guard layers. | `tower`, `futures-core`, `pin-project-lite` |
| `serde` | Disabled | Adds serde support for the `HxEvent` and `LocationOptions` | `serde`, `serde_json` | | `serde` | Disabled | Adds serde support for the `HxEvent` and `LocationOptions` | `serde`, `serde_json` |
<!-- markdownlint-enable --> <!-- markdownlint-enable -->
@ -233,3 +254,6 @@ cargo +nightly test --all-features
- **[Apache License, Version 2.0](/LICENSE-APACHE)** - **[Apache License, Version 2.0](/LICENSE-APACHE)**
at your option. at your option.
[htmx-caching]: https://htmx.org/docs/#caching
[auto-vary-example]: https://github.com/robertwayne/axum-htmx/blob/main/examples/auto-vary.rs

View file

@ -1,3 +1,6 @@
//! A middleware to automatically add a `Vary` header when needed to address
//! [HTMx caching issue](https://htmx.org/docs/#caching)
use std::{ use std::{
sync::Arc, sync::Arc,
task::{Context, Poll}, task::{Context, Poll},
@ -19,10 +22,24 @@ use crate::{
headers::{HX_REQUEST_STR, HX_TARGET_STR, HX_TRIGGER_NAME_STR, HX_TRIGGER_STR}, headers::{HX_REQUEST_STR, HX_TARGET_STR, HX_TRIGGER_NAME_STR, HX_TRIGGER_STR},
HxError, HxError,
}; };
#[cfg(doc)]
use crate::{HxRequest, HxTarget, HxTrigger, HxTriggerName};
const MIDDLEWARE_DOUBLE_USE: &str = const MIDDLEWARE_DOUBLE_USE: &str =
"Configuration error: `axum_httpx::vary_middleware` is used twice"; "Configuration error: `axum_httpx::vary_middleware` is used twice";
/// Addresses [HTMx caching issue](https://htmx.org/docs/#caching)
/// by automatically adding a corresponding `Vary` header when [`HxRequest`], [`HxTarget`],
/// [`HxTrigger`], [`HxTriggerName`] or their combination is used.
#[derive(Clone)]
pub struct AutoVaryLayer;
/// Tower service for [`AutoVaryLayer`]
#[derive(Clone)]
pub struct AutoVaryMiddleware<S> {
inner: S,
}
pub(crate) trait Notifier { pub(crate) trait Notifier {
fn sender(&mut self) -> Option<Sender<()>>; fn sender(&mut self) -> Option<Sender<()>>;
@ -65,9 +82,6 @@ define_notifiers!(
HxTriggerNameExtracted HxTriggerNameExtracted
); );
#[derive(Default, Clone)]
pub struct AutoVaryLayer;
impl<S> Layer<S> for AutoVaryLayer { impl<S> Layer<S> for AutoVaryLayer {
type Service = AutoVaryMiddleware<S>; type Service = AutoVaryMiddleware<S>;
@ -76,11 +90,6 @@ impl<S> Layer<S> for AutoVaryLayer {
} }
} }
#[derive(Clone)]
pub struct AutoVaryMiddleware<S> {
inner: S,
}
impl<S> Service<Request> for AutoVaryMiddleware<S> impl<S> Service<Request> for AutoVaryMiddleware<S>
where where
S: Service<Request, Response = Response> + Send + 'static, S: Service<Request, Response = Response> + Send + 'static,

View file

@ -7,7 +7,7 @@ pub use error::*;
#[cfg(feature = "auto-vary")] #[cfg(feature = "auto-vary")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "auto-vary")))] #[cfg_attr(feature = "unstable", doc(cfg(feature = "auto-vary")))]
mod auto_vary; pub mod auto_vary;
pub mod extractors; pub mod extractors;
#[cfg(feature = "guards")] #[cfg(feature = "guards")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "guards")))] #[cfg_attr(feature = "unstable", doc(cfg(feature = "guards")))]
@ -18,7 +18,7 @@ pub mod responders;
#[cfg(feature = "auto-vary")] #[cfg(feature = "auto-vary")]
#[cfg_attr(feature = "unstable", doc(cfg(feature = "auto-vary")))] #[cfg_attr(feature = "unstable", doc(cfg(feature = "auto-vary")))]
#[doc(inline)] #[doc(inline)]
pub use auto_vary::AutoVaryLayer; pub use auto_vary::*;
#[doc(inline)] #[doc(inline)]
pub use extractors::*; pub use extractors::*;
#[cfg(feature = "guards")] #[cfg(feature = "guards")]