Saltar al contenido

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.

33-typed-action-form.zolo
Playground
// 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.

34-typed-action-form-server.zolo
Playground
// 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.

Índice global

Encuentra tu camino por Zolo

Prueba una idea

Empieza aquí

9 resultados

9 resultados

enespt-br