Routeur avec erreurs
Les handlers retournent une error au lieu de paniquer.
ErrHTTP(404, err) déclenche le bon handler d'erreur.
Routing via http.ServeMux Go 1.22 — paramètres {id} inclus.
Framework HTTP — Go 1.22+ · stdlib uniquement
goWebFlow pose trois pièces sur net/http :
un routeur qui retourne des erreurs, un système layout/page
html/template, et une intégration Vite clé en main.
Zéro dépendance externe.
00 Cycle d'une requête
Du socket au handler — une goroutine par requête.
Chaque couche est lisible, remplaçable, testable indépendamment.
Les handlers retournent une error au lieu de paniquer.
ErrHTTP(404, err) déclenche le bon handler d'erreur.
Routing via http.ServeMux Go 1.22 — paramètres {id} inclus.
Un layout (base.html), des pages (*.html), fusionnés via
{{block "content" .}}.
Hot reload automatique en dev. Données passées directement au template.
{{ vite "src/main.js" }} dans le layout.
En dev : proxy vers localhost:5173.
En prod : hashes depuis le manifest. Aucune config supplémentaire.
Un type App, une Config, et des handlers net/http standard.
package main
import (
"log"
"net/http"
"github.com/Florian418/gowebflow/pkg/httpd"
)
func main() {
app, err := httpd.New(httpd.Config{
LayoutDir: "./ui/layouts",
PageDir: "./ui/pages",
StaticDir: "./ui/dist",
StaticURL: "/static/",
ActiveTheme: "gwfer",
Dev: true,
})
if err != nil {
log.Fatal(err)
}
app.Get("/", func(w http.ResponseWriter, r *http.Request) error {
return app.Render(w, "home.html", nil)
})
app.NotFound(func(w http.ResponseWriter, r *http.Request) error {
w.WriteHeader(http.StatusNotFound)
return app.Render(w, "404.html", nil)
})
log.Fatal(app.Listen(":8080"))
}
Signature func(w http.ResponseWriter, r *http.Request) error —
net/http pur, sans type propriétaire. L'erreur remonte au dispatcher au lieu de paniquer.
app.NotFound() branche un handler Go standard.
Même signature, même logique — ici on rend une page template comme n'importe quelle route.
ActiveTheme pointe vers le sous-dossier du build Vite.
En dev : proxy automatique vers localhost:5173.
En prod : hashes lus depuis manifest.json.
Les handlers retournent une error — le framework l'intercepte, la logge, et appelle le bon handler. Masquer un 500 en 404 est un choix explicite, pas un défaut.
// 404 — chemin dispo en data dans le template
app.NotFound(func(w http.ResponseWriter, r *http.Request) error {
w.WriteHeader(http.StatusNotFound)
return app.Render(w, "404.html", r.URL.Path)
})
// 500 masqué en 404 — l'erreur reste loggée côté serveur
app.OnError(http.StatusInternalServerError, func(w http.ResponseWriter, r *http.Request) error {
w.WriteHeader(http.StatusNotFound)
return app.Render(w, "404.html", nil)
})
// Déclencher un code précis depuis une route
app.Get("/admin", func(w http.ResponseWriter, r *http.Request) error {
if !isAdmin(r) {
return httpd.ErrHTTP(http.StatusForbidden, fmt.Errorf("forbidden"))
}
return app.Render(w, "admin.html", nil)
})
Go envoie un 200 automatiquement dès que le premier octet est écrit dans le body.
w.WriteHeader(code) doit toujours précéder Render dans un handler d'erreur.
Renvoyer 404 pour un 500 ou un 403 est une décision de sécurité — ne pas révéler si la ressource existe ou si le serveur a crashé. L'erreur réelle est loggée par wrap(), seul le code HTTP côté client change.
ErrHTTP(code, err) signale au framework quel handler OnError appeler.
Tout code HTTP est valide — le handler décide ensuite de ce que le navigateur reçoit réellement.
gwf crée la structure complète — layouts, pages, Air, Vite optionnel. Le routing, les templates et Vite sont câblés. Le reste — JSON, sessions, auth — reste en stdlib, à vous.