Create A Simple RESTful API With GoLang

用GO语言构建一个简单的RESTful API

作者:Nic Raboy 2016.7.11 GO语言
原文:https://www.thepolyglotdeveloper.com/2016/07/create-a-simple-restful-api-with-golang/
翻译:一花一世界
时间:2016-09-09
排版:马克飞象(https://maxiang.io

`

翻译前言
本人根据原文翻译过来,翻译过程中感觉一些常用词语难以用简单的词语表达,深知翻译不易。
如有不当之处,请指教,我会及时更新过来。
另外我也把生词附在文后,以后主要翻译有关GO语言的技术文章,主要以短小的为主。

`


正文

现在大多数应用程序在前后台已经分开了。 后台是一典型的RESTful API,也是全栈开发的关键部分。这些API会通常被分成多个路由集合, 它通常也指后台。 构建一个像这样API的应用,通常比把所有的杂揉到单一的应用中,则更为简洁和可维护。

我用各种编程语言实现了RESTful API, 例如Node.js、java, 但是近来我研究了很多GO语言编程。 由于它是一个快速、可靠、简洁编程语言,
因为这个特点,让GO语言看上去适合开发RESTful API, 这个特点也指代GOLang.

我们接下来看下用GO语言构建一个基本的CRUD操作究竟用到了什么。

为了简明, 我们不准备用真实的数据库, 我们会硬编码来作为模拟数据。这让我们重点关注核心的地方,像定义后台、基本的操作。

假设GO语言已经安装和配置在你的机器上了。 从(MAX and Linux)终端,或是windows命令提示行中执行如下:
Create New GoLang Project
创建一个Golang项目:

mkdir -p $GOPATH/src/github.com/nraboy/restapi

如果你命令行中不支持以上的任何一种, 只能自己找到方法手工创建目录
在刚才创建的工程中, 将创建一个包含主函数的文件;在本例中, 我们将用

$GOPATH/src/github.com/nraboy/restapi/myproject.go

在这个Go文件中, 我们将增加如下代码。 用许多代码,不用担心, 接下来我们会把它拆开。

$GOPATH/src/github.com/nraboy/restapi/myproject.go

package main

imprt (
    "encoding/json"
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

type Person struct {
    ID        string   `json:"id,omitempty"`
    Firstname string   `json:"firstname,omitempty"`
    Lastname  string   `json:"lastname,omitempty"`
    Address   *Address `json:"address,omitempty"`
}

type Address struct {
    City  string `json:"city,omitempty"`
    State string `json:"state,omitempty"`
}

var people []Person

func GetPersonEndpoint(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)
    for _, item := range people {
        if item.ID == params["id"] {
            json.NewEncoder(w).Encode(item)
            return
        }
    }
    json.NewEncoder(w).Encode(&Person{})
}

func GetPeopleEndpoint(w http.ResponseWriter, req *http.Request) {
    json.NewEncoder(w).Encode(people)
}

func CreatePersonEndpoint(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)
    var person Person
    _ = json.NewDecoder(req.Body).Decode(&person)
    person.ID = params["id"]
    people = append(people, person)
    json.NewEncoder(w).Encode(people)
}

func DeletePersonEndpoint(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)
    for index, item := range people {
        if item.ID == params["id"] {
            people = append(people[:index], people[index+1:]...)
            break
        }
    }
    json.NewEncoder(w).Encode(people)
}

func main() {
    router := mux.NewRouter()
    people = append(people, Person{ID: "1", Firstname: "Nic", Lastname: "Raboy", Address: &Address{City: "Dublin", State: "CA"}})
    people = append(people, Person{ID: "2", Firstname: "Maria", Lastname: "Raboy"})
    router.HandleFunc("/people", GetPeopleEndpoint).Methods("GET")
    router.HandleFunc("/people/{id}", GetPersonEndpoint).Methods("GET")
    router.HandleFunc("/people/{id}", CreatePersonEndpoint).Methods("POST")
    router.HandleFunc("/people/{id}", DeletePersonEndpoint).Methods("DELETE")
    log.Fatal(http.ListenAndServe(":12345", router))
}

上面的代码究竟是什么鬼?

首先注意到我们导入了各种依赖包。 我们用JSON的数据格式工作, 因此要求有JSON编码包。 我们用HTTP来请求, 但net/http包是不一定要求。 mux包是个好帮手,更容易创建后台, 而且有更多的特性可用。 因为这是一个扩展包, 像下面这样下载:

go get github.com/gorilla/mux

关于mux更多的信息可以参考官方文档。

随着依赖包导入, 就可以创建结构对象,装载我们的数据,我们计划存储的数据是“人”的数据:

$GOPATH/src/github.com/nraboy/restapi/myproject.go
Go

type Person struct {
    ID        string   `json:"id,omitempty"`
    Firstname string   `json:"firstname,omitempty"`
    Lastname  string   `json:"lastname,omitempty"`
    Address   *Address `json:"address,omitempty"`
}

我们注意到, 在结构体中的属性, 同时我们也定义了一个标签,这标签说明数据怎样以JSON的形式存储。在每个标签里,有一个omitempty参数。 这意味着如果这个属性为空, 将会从JSON中排除,而不是显示一个空的字符串或值。

在Person结构体中, 有一个adress的指针。 这说明有一个嵌套的JSON对象, 它必需是一个指针,否则 omitempty会失败。 那么Adress长成什么样呢?

$GOPATH/src/github.com/nraboy/restapi/myproject.go

type Address struct {
    City  string `json:"city,omitempty"`
    State string `json:"state,omitempty"`
}

这里又是一个嵌套结构,但不同于前面说的。

因为不用数据库, 我们在工程中创建了一个全局变量。在应用中,这个变量是一个Person的切片,包含了所有用到的数据。

最简单的后台应用可能是GetPeopleEndpoint, 因为它会把完整的person值返回给前台。 我们将从 插入、删除和获取指定的记录开始。

$GOPATH/src/github.com/nraboy/restapi/myproject.go
Go


func GetPersonEndpoint(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)
    for _, item := range people {
        if item.ID == params["id"] {
            json.NewEncoder(w).Encode(item)
            return
        }
    }
    json.NewEncoder(w).Encode(&Person{})
}

在上面的GetPersonEndpoint中,得到一个整条记录。mux库可以从传入请求的数据中取得任何参数, 然后循环全局切片,匹配请求中指定的ID。 如果匹配到, 则解码JSON后显示出来,否则返回一个空的JSON对象。

现实中,后台会和数据库交互, 也可能包含一些应用逻辑。 这或许就是你反而要用一些查询的原因。
CreatePersonEndpoint 有点不同,因为从申请中接收一个JSON数据格式来工作。

$GOPATH/src/github.com/nraboy/restapi/myproject.go


func CreatePersonEndpoint(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)
    var person Person
    _ = json.NewDecoder(req.Body).Decode(&person)
    person.ID = params["id"]
    people = append(people, person)
    json.NewEncoder(w).Encode(people)
}

在上面中, 根据传入的JSON,解码后存在Person的对象中。 我们会依据mux找的Id分配一个新的对象,然后添加到全局的切片中。 最后, 返回全局数组包,它包含刚才新创建的内容。
在上述例子的场景中, 删除数据的方法也有点不一样。

$GOPATH/src/github.com/nraboy/restapi/myproject.go


func DeletePersonEndpoint(w http.ResponseWriter, req *http.Request) {
    params := mux.Vars(req)
    for index, item := range people {
        if item.ID == params["id"] {
            people = append(people[:index], people[index+1:]...)
            break
        }
    }
    json.NewEncoder(w).Encode(people)
}

在这种情况下,DeletePersonEndpoint 遍历所有的数据和之前看到的GetPersonEndpoint 有点相似。不同的是我们需要删除它,而不是打印它。当找到的Id被删除后, 我们会重新创建切片,不包含刚才的删除的数据。
Finally we end up in our runnable main function that brings the application together.
最后, 我们把所有应用放在一个可执行的主函数中

$GOPATH/src/github.com/nraboy/restapi/myproject.go

func main() {
    router := mux.NewRouter()
    people = append(people, Person{ID: "1", Firstname: "Nic", Lastname: "Raboy", Address: &Address{City: "Dublin", State: "CA"}})
    people = append(people, Person{ID: "2", Firstname: "Maria", Lastname: "Raboy"})
    router.HandleFunc("/people", GetPeopleEndpoint).Methods("GET")
    router.HandleFunc("/people/{id}", GetPersonEndpoint).Methods("GET")
    router.HandleFunc("/people/{id}", CreatePersonEndpoint).Methods("POST")
    router.HandleFunc("/people/{id}", DeletePersonEndpoint).Methods("DELETE")
    log.Fatal(http.ListenAndServe(":12345", router))
}

上面代码中, 首先创建一个新路由和增加两个对象到切片中。 接下来创建后台,它可以调用后台的函数。 注意我们用了GET, POST和适当的DELETE
我们也定义了传入的参数。

最后定义了服务端口12345, 这就接近尾声了。 想要运行并试下效果,需要一个工具,如Postman 或是cURL来测试所有的后台。

结论

以上你看到了怎样用go语言构建一个简单的RESTful API 。 然后用了模拟数据代替数据库, 还看到怎样用GO语言切片和JSOn创建各种后台应用。 在这个例子中,像web、手机、全互联loT通过创建API,你的应用程序易维护简、可扩展,只需要不同的前台而不需要再创建新后台了。


以下是自己的测试:

测试

1. 用firefox下的一个插件poster
2. poster工具

3. GET 所有

4. GET 其中一个Id

5. ADD

6. 删除

生词

  1. be critical of
  2. backend
  3. frontend
  4. Where things start to change
  5. be critical part of
  6. make sense of
  7. be referred to as
  8. what it takes to
  9. house 动词
  10. nested
  11. some form of
  12. In the scenario of
  13. similarly
  14. end up in
  15. give it a shot
  16. brings our project to a close
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值