28 · Servidor HTTP
Formularios de Action Tipados
Envía campos escalares a una @action con HTML nativo y conversión en el servidor desde su firma.
En esta página
Un formulario progresivo funciona antes de iniciar JavaScript. Vincula
action a una función @action y asigna a cada control el nombre del parámetro
correspondiente. El compilador genera el endpoint POST y un campo oculto con la
ruta local de retorno.
Renderiza un formulario cuya acción recibe un entero y un booleano de checkbox.
// Feature: progressive forms take their field types from an @action signature.
// Run with: zolo run examples/features/36-web/33-typed-action-form.zolo --no-cache
use std::html::{View, label, input, button, render}
@action
fn save(age: int, newsletter: bool) -> Result<str, str> {
if age < 18 {
return Result::Err("You must be at least 18")
}
return Result::Ok("Age: {age}; newsletter: {newsletter}")
}
fn preferences() -> View {
<form action={save} return_to="/saved" id="preferences">
<label for="age">Age</label>
<input id="age" name="age" type="number" required />
<label for="newsletter">Receive the newsletter</label>
<input id="newsletter" name="newsletter" type="checkbox" />
<button type="submit">Save</button>
</form>
}
print(render(preferences()))
El formulario renderizado usa HTML normal. Conserva su id, convierte age
a int, y un newsletter sin marcar se convierte en false.
No necesita conversión manual, una isla ni un script de cliente.
| Campos enviados | Comportamiento del servidor |
|---|---|
age=25&newsletter=on |
Llama a save(25, true). |
age=25 |
Llama a save(25, false). |
age=abc, edad vacía o ausente |
HTTP 422 por campo; la acción no se ejecuta. |
age=25&age=26 |
HTTP 422 por campo duplicado; la acción no se ejecuta. |
age=17 |
Llama a la acción; su regla devuelve Result::Err. |
Los tipos admitidos son str, int, float y bool.
Una cadena debe estar presente, pero puede estar vacía. Un entero debe caber
en el rango exacto de 64 bits con signo; un float debe ser finito.
Los booleanos aceptan on/true/1 o off/false/0; omitir el campo
significa false. El servidor convierte los valores incluso si se omite
required o la validación del navegador. Los valores predeterminados de los
parámetros no rellenan campos omitidos.
Para probar el flujo completo, ejecuta este servidor con la CLI local y abre
http://localhost:3099. Continúa ejecutándose hasta que lo detengas:
Una página de formulario, una acción tipada del servidor y una página de éxito.
// Feature: a typed progressive form works through a normal browser POST.
// Run with: zolo run examples/features/36-web/34-typed-action-form-server.zolo --no-cache
// Open http://localhost:3099. No client script or island is needed.
use std::http
use std::html::{View, document, head, title, body, main, h1, p, label, input, button, a}
@action
fn save(age: int, newsletter: bool) -> Result<str, str> {
if age < 18 {
return Result::Err("You must be at least 18")
}
print("Age: {age}; newsletter: {newsletter}")
return Result::Ok("Saved")
}
fn home(_req) -> View {
<document lang="en">
<head><title>Typed progressive form</title></head>
<body><main>
<h1>Preferences</h1>
<form action={save} return_to="/saved">
<label for="age">Age</label>
<input id="age" name="age" type="number" required />
<label for="newsletter">Receive the newsletter</label>
<input id="newsletter" name="newsletter" type="checkbox" />
<button type="submit">Save</button>
</form>
</main></body>
</document>
}
fn saved(_req) -> View {
<document lang="en">
<head><title>Saved</title></head>
<body><main>
<h1>Saved</h1>
<p>The action accepted the submitted values.</p>
<a href="/">Back to preferences</a>
</main></body>
</document>
}
let app = http.router()
|> http.get("/", home)
|> http.get("/saved", saved)
http.serve(3099, app)
Requiere la CLI o el host de Zolo; ábrelo en el playground o ejecútalo localmente.
Envía edad 25 con el checkbox sin marcar. El handler imprime
Age: 25; newsletter: false; el navegador sigue HTTP 303 hasta /saved.
Una solicitud directa utiliza la misma ruta:
curl -i --data-urlencode "age=25" \
--data-urlencode "__zolo_return_to=/saved" \
http://localhost:3099/_zolo/action/features.36-web.34-typed-action-form-server.save
Este endpoint pertenece al archivo del ejemplo: la ruta incluye la identidad
del módulo y el nombre de la acción. En el markup, pasa solo la referencia de
la función con action={save}. El compilador escribe la URL correcta en el
atributo action del formulario renderizado; no la reconstruyas solo con el
nombre de la función.
Los errores de conversión y los resultados de negocio Result::Err devuelven
HTTP 422. Este contrato no muestra errores automáticamente junto a los campos
ni añade estado de envío. Archivos, arrays, objetos, valores opcionales,
parámetros variádicos y sin tipo necesitan otro endpoint o un contrato posterior.
return_to usa / por defecto y admite solo rutas locales absolutas.
Se permite method="post" explícito; otros métodos se rechazan.
Un action="/subscribe" con URL mantiene el comportamiento HTML normal.
El helper anterior web.action_form(save, return_to: "/saved") { ... } sigue
disponible con la misma conversión tipada.
Consulta también