Ir para o conteúdo
Trilha
Capítulos

Aprender

Páginas e rotas

Como pastas viram URLs, incluindo segmentos dinâmicos, catch-all e grupos.

Você já viu que app/eventos/page.go responde /eventos. Este capítulo cobre o resto do mapeamento: parâmetros na URL, caminhos de tamanho variável e pastas que agrupam sem aparecer na URL.

Segmento dinâmico: nome_#

Cada evento terá uma página própria em /eventos/encontro-go. Em vez de uma pasta por evento, crie uma pasta cujo nome termina com _:

app/eventos/slug_/page.go   →   GET /eventos/{slug}

Dentro da página, o valor vem de c.Param:

package slug

import (
	"github.com/emersonjoe/trilha"
	"github.com/emersonjoe/trilha/h"
)

func Page(c *trilha.Ctx) (h.Node, error) {
	slug := c.Param("slug")
	c.SetTitle("Evento " + slug)
	return h.H1(h.Textf("Evento: %s", slug)), nil
}

O nome do parâmetro é o nome da pasta sem o _. Uma pasta id_c.Param("id").

Catch-all: nome__#

Duas barras no final capturam tudo o que vier depois, com as barras internas:

app/docs/caminho__/page.go   →   GET /docs/{caminho...}

GET /docs/guia/instalacao chega com c.Param("caminho") == "guia/instalacao". Uma pasta catch-all precisa ser folha: nada pode existir abaixo dela.

Quem vence quando há empate#

Rotas literais vencem as dinâmicas. Com app/eventos/novo/page.go e app/eventos/slug_/page.go, /eventos/novo vai para a primeira e /eventos/qualquer-outra para a segunda. Duas pastas dinâmicas irmãs (a_ e b_ no mesmo nível) são um erro de geração, porque não haveria como escolher.

Grupos de rota: nome-#

Às vezes você quer que várias páginas compartilhem um layout ou um middleware sem que isso apareça na URL. Uma pasta terminada em - é um grupo:

app/organizador-/middleware.go    ← vale para tudo abaixo
app/organizador-/painel/page.go   → GET /painel   (sem "organizador" na URL)
app/organizador-/eventos/page.go  → GET /eventos  ✗ conflita com app/eventos/page.go

O gerador recusa duas pastas que produzam a mesma URL (E_DUPLICATE_ROUTE), então o segundo exemplo acima não compila.

Deixando a CLI fazer a tradução#

Nada acima precisa ser digitado à mão. O trilha generate recebe a URL e grava a pasta que a convenção pede, já compilando:

trilha generate page /eventos/{slug}   # app/eventos/slug_/page.go
trilha generate route /api/eventos     # app/api/eventos/route.go

A página já vem com c.Param("slug") lido, e o trilha_gen.go é regerado no fim, então a URL responde antes de você abrir o editor. Com --methods, --bind e --form o esqueleto vem também com o contrato — os handlers, a struct, a validação e o formulário — e o trilha generate test <url> escreve o teste ao lado. As flags estão em CLI.

O que o gerador faz com isso#

Rode trilha routes a qualquer momento para ver a tabela:

MÉTODOS   PADRÃO             ORIGEM
GET       /                  app/page.go
GET       /eventos           app/eventos/page.go
GET       /eventos/{slug}    app/eventos/slug_/page.go
GET       /painel            app/organizador-/painel/page.go

Essa tabela vira código Go em trilha_gen.go: um a.Register(trilha.Route{...}) por linha, importando cada pacote. Se você renomear Page, é o compilador quem reclama, não o servidor em produção.

Desafio#

Crie a página de detalhe app/eventos/slug_/page.go que mostre o slug, e uma página app/eventos/hoje/page.go. Confirme com trilha routes que /eventos/hoje aponta para a pasta literal e não para a dinâmica.

Mostrar solução

As duas páginas seguem o formato de Page. A saída de trilha routes deve conter:

GET  /eventos/hoje      app/eventos/hoje/page.go
GET  /eventos/{slug}    app/eventos/slug_/page.go

A ordem alfabética coloca /eventos/hoje antes, mas o que decide a precedência é o roteador: literal antes de dinâmico, sempre.