esponse := JsonResponse{Data: book}
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/books", BookIndex)
router.GET("/books/:isdn", BookShow)
// Create a couple of sample Book entries
bookstore["123"] = &Book{
ISDN: "123",
Title: "Silence of the Lambs",
Author: "Thomas Harris",
Pages: 367,
}
bookstore["124"] = &Book{
ISDN: "124",
Title: "To Kill a Mocking Bird",
Author: "Harper Lee",
Pages: 320,
}
log.Fatal(http.ListenAndServe(":8080", router))
}
如果您现在尝试请求 GET https:// localhost:8080/books
,您将得到以下响应:
{
"meta": null,
"data": [
{
"isdn": "123",
"title": "Silence of the Lambs",
"author": "Thomas Harris",
"pages": 367
},
{
"isdn": "124",
"title": "To Kill a Mocking Bird",
"author": "Harper Lee",
"pages": 320
}
]
}
我们在 main
函数中硬编码了这两个 book 实体。点击这里[2]获取当前阶段的代码。
让我们来重构一下代码。 到目前为止,我们所有的代码都放置在同一个文件中:main.go
。我们可以把它们移到各个单独的文件中。此时我们有一个目录:
.
├── handlers.go
├── main.go
├── models.go
└── responses.go
我们把所有与 JSON
响应相关的结构体移动到 responses.go
,将 handler 函数移动到 Handlers.go
,且将 Book
结构体移动到 models.go
。点击这里[3]查看当前阶段的代码。 现在,我们跳过来写一些测试。在 Go 中,*_test.go
文件是用于测试的。因此让我们创建一个 handlers_test.go
。
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/julienschmidt/httprouter"
)
func TestBookIndex(t *testing.T) {
// Create an entry of the book to the bookstore map
testBook := &Book{
ISDN: "111",
Title: "test title",
Author: "test author",
Pages: 42,
}
bookstore["111"] = testBook
// A request with an existing isdn
req1, err := http.NewRequest("GET", "/books", nil)
if err != nil {
t.Fatal(err)
}
rr1 := newRequestRecorder(req1, "GET", "/books", BookIndex)
if rr1.Code != 200 {
t.Error("Expected response code to be 200")
}
// expected response
er1 := "{\"meta\":null,\"data\":[{\"isdn\":\"111\",\"title\":\"test title\",\"author\":\"test author\",\"pages\":42}]}\n"
if rr1.Body.String() != er1 {
t.Error("Response body does not match")
}
}
// Mocks a handler and returns a httptest.ResponseRecorder
func newRequestRecorder(req *http.Request, method string, strPath string, fnHandler func(w http.ResponseWriter, r *http.Request, param httprouter.Params)) *httptest.ResponseRecorder {
router := httprouter.New()
router.Handle(method, strPath, fnHandler)
// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
router.ServeHTTP(rr, req)
return rr
}
我们使用 httptest
包的 Recorder 来 mock handler。同样,您也可以为 handler BookShow
编写测试。
让我们稍微做些重构。我们仍然把所有路由都定义在了 main
函数中,handler 看起来有点臃肿,我们可以做点 DRY,我们