Pular para o conteúdo

28 · Servidor HTTP

Formulários de Action Tipados

Envie campos escalares para uma @action com HTML nativo e conversão no servidor pela assinatura.

Nesta página

Um formulário progressivo funciona antes de iniciar o JavaScript. Associe action a uma função @action e dê a cada controle o nome do parâmetro correspondente. O compilador gera o endpoint POST e um campo oculto com o caminho local de retorno.

Renderize um formulário cuja ação recebe um inteiro e um 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()))

O formulário renderizado usa HTML normal. Seu id é preservado, age é convertido para int, e newsletter desmarcado vira false. Não precisamos de conversão manual, ilha ou script de cliente.

Campos enviados Comportamento do servidor
age=25&newsletter=on Chama save(25, true).
age=25 Chama save(25, false).
age=abc, idade vazia ou ausente HTTP 422 por campo; a ação não executa.
age=25&age=26 HTTP 422 por campo duplicado; a ação não executa.
age=17 Chama a ação; a regra de negócio devolve Result::Err.

Os tipos suportados são str, int, float e bool. Texto precisa estar presente, mas pode ser vazio. Um inteiro precisa caber no intervalo exato de 64 bits com sinal; um float precisa ser finito. Booleanos aceitam on/true/1 ou off/false/0; omitir significa false. A conversão acontece no servidor mesmo quando alguém ignora required ou as verificações de tipo do navegador. Valores padrão dos parâmetros não preenchem campos omitidos.

Para testar o fluxo completo, rode este servidor na CLI local e abra http://localhost:3099. Ele continua executando até você pará-lo:

Uma página de formulário, uma ação tipada no servidor e uma página de sucesso.

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)

Requer a CLI/host do Zolo — abra no playground ou rode localmente.

Envie idade 25 com o checkbox desmarcado. O handler imprime Age: 25; newsletter: false; o navegador segue o HTTP 303 até /saved. Uma requisição direta usa o mesmo caminho:

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

Esse endpoint pertence ao arquivo deste exemplo: o caminho inclui a identidade do módulo e o nome da ação. No markup, passe apenas a referência da função com action={save}. O compilador escreve a URL correta no atributo action do formulário renderizado; não a reconstrua apenas pelo nome da função.

Falhas de conversão e resultados de negócio Result::Err devolvem HTTP 422. Esse contrato não exibe erros automaticamente ao lado dos campos nem adiciona estado de envio. Uploads, arrays, objetos, valores opcionais, parâmetros variádicos e sem tipo precisam de outro endpoint ou de um contrato posterior.

return_to usa / por padrão e aceita apenas caminhos locais absolutos. method="post" explícito é permitido; outros métodos são rejeitados. action="/subscribe" com URL mantém o comportamento HTML normal. O helper anterior web.action_form(save, return_to: "/saved") { ... } continua disponível com a mesma conversão tipada.

Índice global

Encontre seu caminho pela Zolo

Experimente uma ideia

Comece por aqui

9 resultados

9 resultados

enespt-br