Skip to content

Commit

Permalink
Now rendering json on request instead of html
Browse files Browse the repository at this point in the history
  • Loading branch information
xpmatteo committed Feb 7, 2024
1 parent 468a387 commit 02c0035
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 20 deletions.
28 changes: 10 additions & 18 deletions web/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func MakeIndexHandler(templ *template.Template, model *todo.List) http.Handler {
http.Error(w, "Not found", http.StatusNotFound)
return
}
data := viewModel(model, r)
executeTemplate(w, templ, data)
vm := viewModel(model, r)
render(w, r, templ, vm)
})
}

Expand All @@ -32,8 +32,8 @@ func MakeNewItemHandler(templ *template.Template, model *todo.List) http.Handler
return
}
model.Add(r.Form.Get("new-todo"))
data := viewModel(model, r)
executeTemplate(w, templ, data)
vm := viewModel(model, r)
render(w, r, templ, vm)
})
}

Expand Down Expand Up @@ -61,8 +61,8 @@ func MakeToggleHandler(templ *template.Template, model *todo.List) http.Handler
return
}

data := viewModel(model, r)
executeTemplate(w, templ, data)
vm := viewModel(model, r)
render(w, r, templ, vm)
})
}

Expand Down Expand Up @@ -94,8 +94,8 @@ func MakeEditHandler(templ *template.Template, model *todo.List) http.Handler {
}
}

data := viewModel(model, r)
executeTemplate(w, templ, data)
vm := viewModel(model, r)
render(w, r, templ, vm)
})
}

Expand All @@ -113,19 +113,11 @@ func MakeDestroyHandler(templ *template.Template, model *todo.List) http.Handler
}
model.Destroy(id)

data := viewModel(model, r)
executeTemplate(w, templ, data)
vm := viewModel(model, r)
render(w, r, templ, vm)
})
}

func badRequest(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
}

func executeTemplate(w http.ResponseWriter, templ *template.Template, data map[string]interface{}) {
err := templ.Execute(w, data)
if err != nil {
http.Error(w, "Error rendering template", http.StatusInternalServerError)
return
}
}
38 changes: 38 additions & 0 deletions web/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package web

import (
"encoding/json"
"html/template"
"net/http"
)

const (
headerKeyAccept = "accept"
)

func render(w http.ResponseWriter, r *http.Request, templ *template.Template, model ViewModel) {
if acceptsJson(r) {
w.Header().Add("content-type", "application/json")
err := json.NewEncoder(w).Encode(model)
if err != nil {
panic(err.Error())
}
return
}
err := templ.Execute(w, model)
if err != nil {
http.Error(w, "Error rendering template", http.StatusInternalServerError)
}
}

func acceptsJson(r *http.Request) bool {
accept := r.Header.Get(headerKeyAccept)

return startsWith("application/json", accept)
}

func startsWith(prefix string, s string) bool {
lenPrefix := len(prefix)

return lenPrefix <= len(s) && s[:lenPrefix] == prefix
}
50 changes: 50 additions & 0 deletions web/render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package web

import (
"github.com/stretchr/testify/assert"
"html/template"
"net/http"
"net/http/httptest"
"testing"
)

func Test_rendersJson(t *testing.T) {
w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)
r.Header.Set(headerKeyAccept, "application/json, */*")
vm := ViewModel{"Foo": 123}

render(w, r, nil, vm)

assert := assert.New(t)
assert.Equal("application/json", w.Header().Get("content-type"))
assert.Equal("{\"Foo\":123}\n", w.Body.String())
}

func Test_rendersHtml(t *testing.T) {
w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)
vm := ViewModel{"Foo": 123}
var templFoo = template.Must(template.New("index").Parse("<p>{{.Foo}}</p>"))

render(w, r, templFoo, vm)

assert := assert.New(t)
assert.Equal("text/html; charset=utf-8", w.Header().Get("content-type"))
assert.Equal("<p>123</p>", w.Body.String())
}

func Test_failsRenderingTemplate(t *testing.T) {
w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)
vm := ViewModel{"Foo": 123}
var templFoo = template.Must(template.New("broken").Parse("{{ len .Foo }}"))

render(w, r, templFoo, vm)

assert.Equal(t, 500, w.Code)
}

func Test_startsWith(t *testing.T) {
assert := assert.New(t)
assert.True(startsWith("ab", "abc"))
assert.False(startsWith("xx", "abc"))
assert.False(startsWith("abc", "ab"))
}
6 changes: 4 additions & 2 deletions web/viewModel.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import (
"net/http"
)

func viewModel(model *todo.List, r *http.Request) map[string]interface{} {
type ViewModel map[string]interface{}

func viewModel(model *todo.List, r *http.Request) ViewModel {
items := model.AllItems()
path := determinePath(r)
if path == pathCompleted {
items = model.CompletedItems()
} else if path == pathActive {
items = model.ActiveItems()
}
return map[string]interface{}{
return ViewModel{
"Items": items,
"Path": path,
"ItemsCount": len(model.Items),
Expand Down

0 comments on commit 02c0035

Please sign in to comment.