🐛 (web): HttpRequest clona ahora las extensiones
Documenta el nuevo comportamiento y aprovecha para pulir varios comentarios y restringir visibilidad de símbolos internos.
This commit is contained in:
parent
370bc8ee47
commit
6f82da220b
11 changed files with 55 additions and 56 deletions
|
|
@ -10,8 +10,8 @@ impl Extension for HelloName {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn hello_name(
|
async fn hello_name(
|
||||||
web::Path(name): web::Path<String>,
|
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
|
web::Path(name): web::Path<String>,
|
||||||
) -> Result<Markup, ErrorPage> {
|
) -> Result<Markup, ErrorPage> {
|
||||||
Page::new(request)
|
Page::new(request)
|
||||||
.with_child(Html::with(move |_| {
|
.with_child(Html::with(move |_| {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use std::sync::Arc;
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Para renderizar contenido que dependa del contexto, se puede acceder a él dentro del *closure*:
|
/// Para renderizar contenido que dependa del contexto, se puede acceder a él dentro del closure:
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ where
|
||||||
|
|
||||||
/// Despacha las funciones asociadas a una [`ActionKey`] con posible salida anticipada.
|
/// Despacha las funciones asociadas a una [`ActionKey`] con posible salida anticipada.
|
||||||
///
|
///
|
||||||
/// Funciona igual que [`dispatch_actions`], pero el *closure* puede devolver
|
/// Funciona igual que [`dispatch_actions`], pero el closure puede devolver
|
||||||
/// [`std::ops::ControlFlow::Continue`] para continuar ejecutando la siguiente acción; o
|
/// [`std::ops::ControlFlow::Continue`] para continuar ejecutando la siguiente acción; o
|
||||||
/// [`std::ops::ControlFlow::Break`] para detener la iteración inmediatamente.
|
/// [`std::ops::ControlFlow::Break`] para detener la iteración inmediatamente.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,8 @@ impl Children {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Añade un componente hijo al final de la lista.
|
// Añade un componente hijo al final de la lista. También lo usa `core::theme::regions` al
|
||||||
|
// fusionar regiones, fuera de este módulo.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn add(&mut self, child: Child) -> &mut Self {
|
pub(crate) fn add(&mut self, child: Child) -> &mut Self {
|
||||||
self.0.push(child);
|
self.0.push(child);
|
||||||
|
|
|
||||||
|
|
@ -110,15 +110,27 @@ pub trait Component: AnyInfo + ComponentClone + ComponentRender + Send + Sync {
|
||||||
|
|
||||||
/// Genera el marcado HTML del componente cuando ningún tema lo sobrescribe.
|
/// Genera el marcado HTML del componente cuando ningún tema lo sobrescribe.
|
||||||
///
|
///
|
||||||
/// Cuarto paso del [ciclo de renderizado](ComponentRender): se invoca tras
|
/// Este es el cuarto paso del [ciclo de renderizado](ComponentRender) tras llamar al
|
||||||
/// [`setup()`](Self::setup) y la acción
|
/// [`setup()`](Self::setup) del componente y despachar la acción
|
||||||
/// [`BeforeRender`](crate::base::action::component::BeforeRender), pero solamente si ningún
|
/// [`BeforeRender`](crate::base::action::component::BeforeRender) que atiende los cambios de
|
||||||
/// tema en la cadena devuelve `Some` en
|
/// otras extensiones antes de renderizar. Se invoca sólo si ningún tema en la cadena devuelve
|
||||||
/// [`Theme::handle_component()`](crate::core::theme::Theme::handle_component).
|
/// `Some` en [`Theme::handle_component()`](crate::core::theme::Theme::handle_component) para
|
||||||
|
/// este componente.
|
||||||
|
///
|
||||||
|
/// Es `async`, a diferencia de [`setup()`](Self::setup), para permitir a los componentes
|
||||||
|
/// realizar aquí sus llamadas asíncronas, como consultas a base de datos, peticiones a
|
||||||
|
/// servicios externos, o cualquier operación de E/S, que necesiten para preparar su contenido.
|
||||||
///
|
///
|
||||||
/// Se recomienda obtener los datos del componente a través de sus propios métodos para que los
|
/// Se recomienda obtener los datos del componente a través de sus propios métodos para que los
|
||||||
/// temas puedan implementar `handle_component()` sin depender de los detalles internos.
|
/// temas puedan implementar `handle_component()` sin depender de los detalles internos.
|
||||||
///
|
///
|
||||||
|
/// Los campos que representen contenido no deben almacenar [`Markup`] ya generado, sino que
|
||||||
|
/// guardarán el dato en bruto, por ejemplo [`L10n`](crate::locale::L10n) para textos
|
||||||
|
/// traducibles, o un componente anidado como [`Html`](crate::base::component::Html), o
|
||||||
|
/// cualquier otro `Component`, normalmente a través de [`Embed`](crate::core::component::Embed)
|
||||||
|
/// o [`Child`](crate::core::component::Child); y será este método quien lo convierta en HTML en
|
||||||
|
/// el momento del renderizado, no quien construye la instancia.
|
||||||
|
///
|
||||||
/// Por defecto, devuelve un [`Markup`] vacío (`Ok(html! {})`). En caso de error, devuelve un
|
/// Por defecto, devuelve un [`Markup`] vacío (`Ok(html! {})`). En caso de error, devuelve un
|
||||||
/// [`ComponentError`] que puede incluir un marcado alternativo (*fallback*).
|
/// [`ComponentError`] que puede incluir un marcado alternativo (*fallback*).
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,9 @@ impl ComponentError {
|
||||||
|
|
||||||
// **< ComponentError GETTERS >*****************************************************************
|
// **< ComponentError GETTERS >*****************************************************************
|
||||||
|
|
||||||
/// Consume el error y devuelve su marcado alternativo.
|
// Consume el error y devuelve su marcado alternativo. Se invoca internamente desde
|
||||||
///
|
// `ComponentRender::render()`, en el mismo módulo `core::component`.
|
||||||
/// Se invoca internamente en [`ComponentRender`](crate::core::component::ComponentRender).
|
pub(super) fn into_fallback(self) -> Markup {
|
||||||
pub(crate) fn into_fallback(self) -> Markup {
|
|
||||||
self.fallback
|
self.fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ impl JavaScript {
|
||||||
/// Equivale a `<script>...</script>`. El parámetro `name` se usa como identificador interno del
|
/// Equivale a `<script>...</script>`. El parámetro `name` se usa como identificador interno del
|
||||||
/// script.
|
/// script.
|
||||||
///
|
///
|
||||||
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
|
/// La función closure recibirá el [`Context`] por si se necesita durante el renderizado.
|
||||||
pub fn inline<F>(name: impl Into<CowStr>, f: F) -> Self
|
pub fn inline<F>(name: impl Into<CowStr>, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||||
|
|
@ -139,7 +139,7 @@ impl JavaScript {
|
||||||
///
|
///
|
||||||
/// En condiciones normales, los scripts con `defer` se ejecutan antes de `DOMContentLoaded`.
|
/// En condiciones normales, los scripts con `defer` se ejecutan antes de `DOMContentLoaded`.
|
||||||
///
|
///
|
||||||
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
|
/// La función closure recibirá el [`Context`] por si se necesita durante el renderizado.
|
||||||
pub fn on_load<F>(name: impl Into<CowStr>, f: F) -> Self
|
pub fn on_load<F>(name: impl Into<CowStr>, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||||
|
|
@ -153,11 +153,11 @@ impl JavaScript {
|
||||||
/// Crea un **script embebido** con un **handler asíncrono**.
|
/// Crea un **script embebido** con un **handler asíncrono**.
|
||||||
///
|
///
|
||||||
/// El código se envuelve en un `addEventListener('DOMContentLoaded',async()=>{...})`, que
|
/// El código se envuelve en un `addEventListener('DOMContentLoaded',async()=>{...})`, que
|
||||||
/// emplea una función `async` para que el cuerpo devuelto por la función *closure* pueda usar
|
/// emplea una función `async` para que el cuerpo devuelto por la función closure pueda usar
|
||||||
/// `await`. Ideal para hidratar la interfaz, cargar módulos dinámicos o realizar lecturas
|
/// `await`. Ideal para hidratar la interfaz, cargar módulos dinámicos o realizar lecturas
|
||||||
/// iniciales.
|
/// iniciales.
|
||||||
///
|
///
|
||||||
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
|
/// La función closure recibirá el [`Context`] por si se necesita durante el renderizado.
|
||||||
pub fn on_load_async<F>(name: impl Into<CowStr>, f: F) -> Self
|
pub fn on_load_async<F>(name: impl Into<CowStr>, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::{AutoDefault, CowStr, Weight, util};
|
||||||
// Informa al navegador de la naturaleza del recurso para que pueda asignarle la prioridad correcta
|
// Informa al navegador de la naturaleza del recurso para que pueda asignarle la prioridad correcta
|
||||||
// y aplicar la política de caché adecuada.
|
// y aplicar la política de caché adecuada.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub(crate) enum AsType {
|
enum AsType {
|
||||||
// Fuente web (`@font-face`). Implica `crossorigin` automático.
|
// Fuente web (`@font-face`). Implica `crossorigin` automático.
|
||||||
#[default]
|
#[default]
|
||||||
Font,
|
Font,
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ impl StyleSheet {
|
||||||
/// Equivale a `<style>...</style>`. El parámetro `name` se usa como identificador interno del
|
/// Equivale a `<style>...</style>`. El parámetro `name` se usa como identificador interno del
|
||||||
/// recurso.
|
/// recurso.
|
||||||
///
|
///
|
||||||
/// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado.
|
/// La función closure recibirá el [`Context`] por si se necesita durante el renderizado.
|
||||||
pub fn inline<F>(name: impl Into<CowStr>, f: F) -> Self
|
pub fn inline<F>(name: impl Into<CowStr>, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,19 @@ use super::{LanguageIdentifier, langid};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
/// Tabla de idiomas soportados por PageTop.
|
// Tabla de idiomas soportados por PageTop.
|
||||||
///
|
//
|
||||||
/// Cada entrada asocia un código de idioma en minúsculas (por ejemplo, `"en"` o `"es-es"`) con:
|
// Cada entrada asocia un código de idioma en minúsculas (por ejemplo, "en" o "es-es") con:
|
||||||
///
|
//
|
||||||
/// - Su [`LanguageIdentifier`] canónico.
|
// - Su `LanguageIdentifier` canónico.
|
||||||
/// - La clave de traducción definida en `src/locale/{lang}/languages.ftl` para mostrar su nombre en
|
// - La clave de traducción definida en `src/locale/{lang}/languages.ftl` para mostrar su nombre en
|
||||||
/// el idioma activo.
|
// el idioma activo.
|
||||||
///
|
//
|
||||||
/// Esto permite admitir alias de idioma como `"en"` o `"es"` y, al mismo tiempo, mantener un
|
// Esto permite admitir alias de idioma como "en" o "es" y, al mismo tiempo, mantener un
|
||||||
/// identificador de idioma canónico (por ejemplo, `langid!("en-US")` o `langid!("es-ES")`).
|
// identificador de idioma canónico (por ejemplo, `langid!("en-US")` o `langid!("es-ES")`).
|
||||||
pub(crate) static LANGUAGES: LazyLock<HashMap<&str, (LanguageIdentifier, &str)>> =
|
//
|
||||||
|
// Sólo lo usa `locale::definition`, en el mismo módulo `locale`.
|
||||||
|
pub(super) static LANGUAGES: LazyLock<HashMap<&str, (LanguageIdentifier, &str)>> =
|
||||||
LazyLock::new(|| {
|
LazyLock::new(|| {
|
||||||
util::kv![
|
util::kv![
|
||||||
"en" => ( langid!("en-US"), "english" ),
|
"en" => ( langid!("en-US"), "english" ),
|
||||||
|
|
|
||||||
37
src/web.rs
37
src/web.rs
|
|
@ -66,25 +66,12 @@ use std::task::{Context, Poll};
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Orden de parámetros en el handler
|
/// `HttpRequest` no consume las extensiones de la petición, por lo que el resto de extractores del
|
||||||
///
|
/// handler que también las necesiten (como `Path<T>`, `Extension<T>`...) las siguen viendo
|
||||||
/// `HttpRequest` toma las extensiones de la petición al extraerse. Los extractores que leen de ahí
|
/// intactas, sea cual sea la posición en la que se declare `HttpRequest`. Por convención, se
|
||||||
/// (como `Path<T>` o `Extension<T>`) deben aparecer **antes** en la lista de parámetros del
|
/// declara como primer parámetro del handler. El único orden que sigue siendo obligatorio es el que
|
||||||
/// handler:
|
/// impone Axum: un extractor que consuma el cuerpo de la petición (`Form<T>`, `RawForm`...) debe ir
|
||||||
///
|
/// siempre el último.
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop::prelude::*;
|
|
||||||
/// // Correcto: Path<T> se declara antes que HttpRequest.
|
|
||||||
/// async fn view_post(
|
|
||||||
/// web::Path(id): web::Path<u32>,
|
|
||||||
/// request: HttpRequest,
|
|
||||||
/// ) -> Result<Markup, ErrorPage> {
|
|
||||||
/// # todo!()
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Los extractores que no dependen de las extensiones, como `Query<T>` o `Method`, no están sujetos
|
|
||||||
/// a esta restricción.
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct HttpRequest {
|
pub struct HttpRequest {
|
||||||
uri: http::Uri,
|
uri: http::Uri,
|
||||||
|
|
@ -132,20 +119,18 @@ impl HttpRequest {
|
||||||
impl<S: Send + Sync> FromRequestParts<S> for HttpRequest {
|
impl<S: Send + Sync> FromRequestParts<S> for HttpRequest {
|
||||||
type Rejection = Infallible;
|
type Rejection = Infallible;
|
||||||
|
|
||||||
// Extrae la petición y toma las extensiones que han sido inyectadas por middleware. Las
|
// Clona (no toma) las extensiones inyectadas por middleware, para que otros extractores del
|
||||||
// extensiones se mueven a un `Arc` compartido para que `HttpRequest` sea `Clone`.
|
// handler (`Path<T>`, `Extension<T>`...) las sigan viendo intactas sin importar en qué posición
|
||||||
//
|
// se declare `HttpRequest`. El clon se envuelve en un `Arc` compartido para que `HttpRequest`
|
||||||
// Nota: tras este extractor `parts.extensions` queda vacío; otros extractores que dependan de
|
// sea `Clone` a coste mínimo en el resto de su ciclo de vida.
|
||||||
// `Extension<T>` deben registrarse antes en la cadena del handler.
|
|
||||||
async fn from_request_parts(
|
async fn from_request_parts(
|
||||||
parts: &mut http::request::Parts,
|
parts: &mut http::request::Parts,
|
||||||
_state: &S,
|
_state: &S,
|
||||||
) -> Result<Self, Self::Rejection> {
|
) -> Result<Self, Self::Rejection> {
|
||||||
let extensions = std::mem::take(&mut parts.extensions);
|
|
||||||
Ok(HttpRequest {
|
Ok(HttpRequest {
|
||||||
uri: parts.uri.clone(),
|
uri: parts.uri.clone(),
|
||||||
headers: parts.headers.clone(),
|
headers: parts.headers.clone(),
|
||||||
extensions: Arc::new(extensions),
|
extensions: Arc::new(parts.extensions.clone()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue