diff --git a/csrf/handlers.go b/csrf/handlers.go
new file mode 100644
index 0000000..9a5ceb2
--- /dev/null
+++ b/csrf/handlers.go
@@ -0,0 +1,56 @@
+package csrf
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/justinas/nosurf"
+)
+
+type user struct {
+ firstName, lastName, email string
+}
+
+var demoUser user = user{
+ firstName: "Joe",
+ lastName: "Blow",
+ email: "joe@blow.com",
+}
+
+func Handlers(prefix string, mux *http.ServeMux) {
+ mux.HandleFunc(prefix+"/", index)
+ mux.HandleFunc(prefix, index)
+ mux.Handle(prefix+"/contact/1", nosurf.New(http.HandlerFunc(putUser)))
+ mux.Handle(prefix+"/contact/1/edit", nosurf.New(http.HandlerFunc(editForm)))
+}
+
+func index(w http.ResponseWriter, r *http.Request) {
+ // Load user
+ user := demoUser
+ if r.Header.Get("HX-Request") == "true" {
+ Display(user).Render(r.Context(), w)
+ return
+ }
+ Index(user).Render(r.Context(), w)
+}
+
+func editForm(w http.ResponseWriter, r *http.Request) {
+ // Load user
+ user := demoUser
+ Form(user, nosurf.Token(r)).Render(r.Context(), w)
+}
+
+func putUser(w http.ResponseWriter, r *http.Request) {
+ // Load user
+ if err := r.ParseForm(); err != nil {
+ log.Println(err)
+ w.WriteHeader(500)
+ return
+ }
+ demoUser = user{
+ firstName: r.FormValue("firstName"),
+ lastName: r.FormValue("lastName"),
+ email: r.FormValue("email"),
+ }
+ Display(demoUser).Render(r.Context(), w)
+}
diff --git a/csrf/templates.templ b/csrf/templates.templ
new file mode 100644
index 0000000..18c2a11
--- /dev/null
+++ b/csrf/templates.templ
@@ -0,0 +1,42 @@
+package csrf
+
+import (
+ "fmt"
+
+ "examples/shared"
+)
+
+templ Display(u user) {
+
+
: { u.firstName }
+
: { u.lastName }
+
: { u.email }
+
+
+}
+
+templ Form(u user, csrfToken string) {
+
+}
+
+templ Index(u user) {
+ @shared.Layout("Click to Edit") {
+ Click to Edit
+ @Display(u)
+ }
+}
+
diff --git a/csrf/templates_templ.go b/csrf/templates_templ.go
new file mode 100644
index 0000000..9c5d4d5
--- /dev/null
+++ b/csrf/templates_templ.go
@@ -0,0 +1,701 @@
+// Code generated by templ@(devel) DO NOT EDIT.
+
+package csrf
+
+//lint:file-ignore SA4006 This context is only used if a nested component is present.
+
+import "github.com/a-h/templ"
+import "context"
+import "io"
+import "bytes"
+
+// GoExpression
+import (
+ "fmt"
+
+ "examples/shared"
+)
+
+func Display(u user) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+ templBuffer, templIsBuffer := w.(*bytes.Buffer)
+ if !templIsBuffer {
+ templBuffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templBuffer)
+ }
+ ctx = templ.InitializeContext(ctx)
+ var_1 := templ.GetChildren(ctx)
+ if var_1 == nil {
+ var_1 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ // Element (standard)
+ _, err = templBuffer.WriteString("")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("")
+ if err != nil {
+ return err
+ }
+ // Text
+ var_3 := `: `
+ _, err = templBuffer.WriteString(var_3)
+ if err != nil {
+ return err
+ }
+ // StringExpression
+ var var_4 string = u.firstName
+ _, err = templBuffer.WriteString(templ.EscapeString(var_4))
+ if err != nil {
+ return err
+ }
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("")
+ if err != nil {
+ return err
+ }
+ // Text
+ var_6 := `: `
+ _, err = templBuffer.WriteString(var_6)
+ if err != nil {
+ return err
+ }
+ // StringExpression
+ var var_7 string = u.lastName
+ _, err = templBuffer.WriteString(templ.EscapeString(var_7))
+ if err != nil {
+ return err
+ }
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("")
+ if err != nil {
+ return err
+ }
+ // Text
+ var_9 := `: `
+ _, err = templBuffer.WriteString(var_9)
+ if err != nil {
+ return err
+ }
+ // StringExpression
+ var var_10 string = u.email
+ _, err = templBuffer.WriteString(templ.EscapeString(var_10))
+ if err != nil {
+ return err
+ }
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ if !templIsBuffer {
+ _, err = io.Copy(w, templBuffer)
+ }
+ return err
+ })
+}
+
+func Form(u user, csrfToken string) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+ templBuffer, templIsBuffer := w.(*bytes.Buffer)
+ if !templIsBuffer {
+ templBuffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templBuffer)
+ }
+ ctx = templ.InitializeContext(ctx)
+ var_12 := templ.GetChildren(ctx)
+ if var_12 == nil {
+ var_12 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ // Element (standard)
+ _, err = templBuffer.WriteString("")
+ if err != nil {
+ return err
+ }
+ if !templIsBuffer {
+ _, err = io.Copy(w, templBuffer)
+ }
+ return err
+ })
+}
+
+func Index(u user) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+ templBuffer, templIsBuffer := w.(*bytes.Buffer)
+ if !templIsBuffer {
+ templBuffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templBuffer)
+ }
+ ctx = templ.InitializeContext(ctx)
+ var_18 := templ.GetChildren(ctx)
+ if var_18 == nil {
+ var_18 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ // TemplElement
+ var_19 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
+ templBuffer, templIsBuffer := w.(*bytes.Buffer)
+ if !templIsBuffer {
+ templBuffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templBuffer)
+ }
+ // Element (standard)
+ _, err = templBuffer.WriteString("")
+ if err != nil {
+ return err
+ }
+ // Text
+ var_20 := `Click to Edit`
+ _, err = templBuffer.WriteString(var_20)
+ if err != nil {
+ return err
+ }
+ _, err = templBuffer.WriteString("
")
+ if err != nil {
+ return err
+ }
+ // Whitespace (normalised)
+ _, err = templBuffer.WriteString(` `)
+ if err != nil {
+ return err
+ }
+ // TemplElement
+ err = Display(u).Render(ctx, templBuffer)
+ if err != nil {
+ return err
+ }
+ if !templIsBuffer {
+ _, err = io.Copy(w, templBuffer)
+ }
+ return err
+ })
+ err = shared.Layout("Click to Edit").Render(templ.WithChildren(ctx, var_19), templBuffer)
+ if err != nil {
+ return err
+ }
+ if !templIsBuffer {
+ _, err = io.Copy(w, templBuffer)
+ }
+ return err
+ })
+}
+
diff --git a/go.mod b/go.mod
index c70bbe1..5bef7ea 100644
--- a/go.mod
+++ b/go.mod
@@ -9,4 +9,7 @@ require (
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4
)
-require golang.org/x/net v0.9.0 // indirect
+require (
+ github.com/justinas/nosurf v1.1.1 // indirect
+ golang.org/x/net v0.9.0 // indirect
+)
diff --git a/go.sum b/go.sum
index fb3442e..7fbd5b9 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,8 @@ github.com/a-h/templ v0.2.233 h1:EnZqZmtV0YICqWG6MtLNmTcWuFkl2ImyQ63SIpWaM2Y=
github.com/a-h/templ v0.2.233/go.mod h1:h1DdzFMWVApvTcZBNmM6+mD6EPq6uYkncMNF7zdLj9I=
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 h1:NeF/iw7KU9W7CYYJimd5x7ooOXCLrfo8FcHdFPUU+2w=
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5/go.mod h1:nqma2qb9ViAJOP4MBucyH+SPbOyNDZaRQyusfpK4PjY=
+github.com/justinas/nosurf v1.1.1 h1:92Aw44hjSK4MxJeMSyDa7jwuI9GR2J/JCQiaKvXXSlk=
+github.com/justinas/nosurf v1.1.1/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 h1:0sw0nJM544SpsihWx1bkXdYLQDlzRflMgFJQ4Yih9ts=
diff --git a/main.go b/main.go
index cba1c90..017f8bc 100644
--- a/main.go
+++ b/main.go
@@ -4,6 +4,7 @@ import (
"examples/bulkupdate"
"examples/clicktoedit"
"examples/clicktoload"
+ "examples/csrf"
"examples/deleterow"
"examples/editrow"
"examples/inlinevalidation"
@@ -37,6 +38,12 @@ type Example struct {
}
var examples = []Example{
+ {
+ Name: "CSRF Protection",
+ Desc: "Demonstrates how to do CSRF Protection",
+ Slug: "csrf",
+ Handlers: csrf.Handlers,
+ },
{
Name: "Click To Edit",
Desc: "Demonstrates inline editing of a data object",