使用Go构建RESTful的JSON API
原文地址
这篇文章不仅仅讨论如何使用Go构建RESTful的JSON API,同时也会讨论如何设计好的RESTful API。如果你曾经遭遇了未遵循良好设计的API,那么你最终将写烂代码来使用这些垃圾API。希望阅读这篇文章后,你能够对好的API应该是怎样的有更多的认识。
在JSON前,XML是一种主流的文本格式。笔者有幸XML和JSON都使用过,毫无疑问,JSON是明显的赢家。本文不会深入涉及JSON API的概念,在jsonapi.org可以找到的详细的描述。
Sponsor NoteSpringOne2GX是一个专门面向App开发者、解决方案和数据架构师的会议。议题都是专门针对程序猿(媛),架构师所使用的流行的开源技术,如:Spring IO Projects,Groovy & Grails,Cloud Foundry,RabbitMQ,Redis,Geode,,Hadoop and Tomcat等。
一个基本的Web Server一个RESTful服务本质上首先是一个Web service。下面的是示例是一个最简单的Web server,对于任何请求都简单的直接返回请求链接:
package main import ( "fmt" "html" "log" "net/http" ) func main() { http.HandleFunc("http://www.mamicode.com/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil)) }编译执行这个示例将运行这个server,监听8080端口。尝试使用:8080访问server。
增加一个路由当大多数标准库开始支持路由,我发现大多数人都搞不清楚它们是如何工作的。我在项目中使用过几个第三方的router。印象最深的是Gorilla Web Toolkit中的mux router.
另一个比较流行的router是Julien Schmidt贡献的httprouter
package main import ( "fmt" "html" "log" "net/http" "github.com/gorilla/mux" ) func main() { router := mux.NewRouter().StrictSlash(true) router.HandleFunc("http://www.mamicode.com/", Index) log.Fatal(http.ListenAndServe(":8080", router)) } func Index(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }运行上面的示例,首先需要安装包“github.com/gorilla/mux”.可以直接使用命令go get遍历整个source code安装所有未安装的依赖包。
译者注:也可以使用go get "github.com/gorilla/mux"直接安装包。
上面的示例创建了一个简单的router,增加了一个“/”路由,并分配Index handler响应针对指定的endpoint的访问。这是你会发现在第一个示例中还能访问的如:8080/foo这类的链接在这个示例中不再工作了,这个示例将只能响应链接:8080.
创建更多的基本路由上一节我们已经有了一个路由,是时候创建更多的路由了。假设我们将要创建一个基本的TODO app。
package main import ( "fmt" "log" "net/http" "github.com/gorilla/mux" ) func main() { router := mux.NewRouter().StrictSlash(true) router.HandleFunc("http://www.mamicode.com/", Index) router.HandleFunc("/todos", TodoIndex) router.HandleFunc("/todos/{todoId}", TodoShow) log.Fatal(http.ListenAndServe(":8080", router)) } func Index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Welcome!") } func TodoIndex(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Todo Index!") } func TodoShow(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) todoId := vars["todoId"] fmt.Fprintln(w, "Todo show:", todoId) }现在我们又在上一个示例的基础上增加了两个routes,分别是:
这就是一个RESTful设计的开始。注意,最后一个路由我们增加了一个名为todoId的变量。这将允许我们向route传递变量,然后获得合适的响应记录。
基本样式有了路由后,就可以创建一些基本的TODO样式用于发送和检索数据。在一些其他语言中使用类(class)来达到这个目的,Go中使用struct。
package main import “time” type Todo struct { Name string Completed tool Due time.time } type Todos []Todo 注:最后一行定义的类型Todos是Todo的slice。稍后你将会看到怎么使用它。
返回JSON基于上面的基本样式,我们可以模拟真实的响应,并基于静态数据列出TodoIndex。
func TodoIndex(w http.ResponseWriter, r *http.Request) { todos := Todos{ Todo{Name: "Write presentation"}, Todo{Name: "Host meetup"}, } json.NewEncoder(w).Encode(todos) }这样就创建了一个Todos的静态slice,并被编码响应用户请求。如果这时你访问:8080/todos,你将得到如下响应:
[ { "Name": "Write presentation", "Completed": false, "Due": "0001-01-01T00:00:00Z" }, { "Name": "Host meetup", "Completed": false, "Due": "0001-01-01T00:00:00Z" } ] 一个稍微好点的样式可能你已经发现了,基于前面的样式,todos返回的并不是一个标准的JSON数据包(JSON格式定义中不包含大写字母)。虽然这个问题有那么一点微不足道,但是我们还是可以解决它:
package main import "time" type Todo struct { Name string `json:"name"` Completed bool `json:"completed"` Due time.Time `json:"due"` } type Todos []Todo上面的代码示例在原来的基础上增加了struct tags,这样可以指定JSON的编码格式。
文件拆分到此我们需要对这个项目稍微做下重构。现在一个文件包含了太多的内容。我们将创建如下几个文件,并重新组织文件内容:
main.go
handlers.go
routes.go
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/63035.html