使用 Golang, chi 和 MySQL 来构建 RESTful API

在我目前的公司,我正在使用微服务架构,而 Golang 是创建微服务方面最受欢迎的语言。 今天,我将为博客文章构建一个简单的 CRUD API Web 服务。 撰写本文的原因是为了帮助初学者构建自己的 REST API。

在此 API 中,我们使用 dep 作为包管理(依赖管理)工具,使用 MySQL 数据库,使用 chi 作为路由。 在项目中使用包管理器是一个好习惯。由于中间件和一些其他优秀的功能,我们选择 chi 包进行路由的管理,你可以浏览它的 文档

在一个通用 CRUD 应用中,API 如下:

  1. POST /posts
  2. GET /posts
  3. GET /posts/{id}
  4. PUT /posts/{id}
  5. DELETE /posts/{id}

*在开始之前,请确保你的 $GOPATH 环境变量正确。然后在 $GOPATH/src 文件夹下新建一个 wiki 文件夹。如果你还未安装 dep 则利用下面的命令安装它。

$ go get -u github.com/golang/dep/cmd/dep

在成功安装了 dep 以后,进入刚刚新建的 wiki 文件夹然后初始化 dep,并且在这里安装一些其他的我们需要的包。

$ dep init
$ dep ensure -add github.com/go-chi/chi github.com/go-sql-driver/mysql

现在在 wiki 文件夹下新建 main.go 文件。然后按照如下代码在 main.go 文件中 导入 chi 和 SQL 包创建所有的路由并配置数据库连接凭据。下面我列出了我自己的代码,你可以根据他修改成你自己所需要的。

go-sql-route.go

package main

import (
    "github.com/go-chi/chi"
    "github.com/go-chi/chi/middleware"
    _ "github.com/go-sql-driver/mysql"
)

var router *chi.Mux
var db *sql.DB

const (
    dbName = "go-mysql-crud"
    dbPass = "12345"
    dbHost = "localhost"
    dbPort = "33066"
)

func routers() *chi.Mux {
    router.Get("/posts", AllPosts)
    router.Get("/posts/{id}", DetailPost)
    router.Post("/posts", CreatePost)
    router.Put("/posts/{id}", UpdatePost)
    router.Delete("/posts/{id}", DeletePost)

    return router
}

很好,让我们建立与数据库的连接并初始化路由器和中间件。 在这里,我们将使用 recover 中间件。 因为这可以保证任何请求失败应用程序都不会死亡,因此用户可以在你不重新启动应用的情况下再次请求。

go-sql-2.go

func init() { 
    router = chi.NewRouter() 
    router.Use(middleware.Recoverer)  

    dbSource := fmt.Sprintf("root:%s@tcp(%s:%s)/%s?charset=utf8",  dbPass, dbHost, dbPort, dbName)

    var err error
    db, err = sql.Open("mysql", dbSource) 

    catch(err)
}

不要纠结上面出现的 catch 函数,我将在之后定义此函数。 dbSource 的完整格式为 username:password@protocol(address)/dbname?param=value

现在我们将创建一个基本的 POST 模型,该模型用于发送和检索数据。 在 Go 语言中,模型通常用 struct 来定义。

go-sql-3.go

type Post struct {
    ID      int    `json: "id"`
    Title   string `json: "title"`
    Content string `json: "content"`
}

通过添加 json 标签,我们可以将 struct 转换为 JSON 格式。 我们将通过终端或其他第三方应用在 MySQL 数据库中创建 go-mysql-crudposts 表。 我主要用的软件是 Sequel Pro。

post.sql

CREATE DATABASE  IF NOT EXISTS `go-mysql-crud` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci */;
USE `go-mysql-crud`;

/* Create table for `posts` */

DROP TABLE IF EXISTS `posts`;
CREATE TABLE `posts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `content` longtext COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
);

还记得我们之前创建的路由和处理函数吗? 现在是实现这些的时候了。 当有人通过 POST 方法并利用 json 格式携带 title 和 content 信息请求我们的 /post 路由时我们的代码将执行 CreatePost 函数。

go-sql-4.go

// CreatePost create a new post
func CreatePost(w http.ResponseWriter, r *http.Request) { 
    var post Post 
    json.NewDecoder(r.Body).Decode(&post)  

    query, err := db.Prepare("Insert posts SET title=?, content=?")
    catch(err) 

    _, er := query.Exec(post.Title, post.Content) 
    catch(er) 
    defer query.Close()  

    respondwithJSON(w, http.StatusCreated, map[string]string{"message": "successfully created"})
}

在上面的代码中,我们使用了 json.NewDecoder 来读取参数 r 中的 POST 数据 并将其解码为我们的 Post struct 模型。 然后准备我们的插入查询并随后执行。

title=? 表示我们动态的标题数据要执行,此数据来源于我们函数所接收到的 post 请求体。 在查询结束以后不要忘记关闭查询。 最后,我们使用 respondwithJSON 函数将消息以 JSON 格式发送给客户端。 稍后我们就实现这个函数。

让我们实现 UpdatePost 和 DeletePost 函数。

go-sql-5.go

// UpdatePost update a  spesific post
func UpdatePost(w http.ResponseWriter, r *http.Request) {
    var post Post
    id := chi.URLParam(r, "id")
    json.NewDecoder(r.Body).Decode(&post)

    query, err := db.Prepare("Update posts set title=?, content=? where id=?")
    catch(err)
    _, er := query.Exec(post.Title, post.Content, id)
    catch(er)

    defer query.Close()

    respondwithJSON(w, http.StatusOK, map[string]string{"message": "update successfully"})

}

// DeletePost remove a spesific post
func DeletePost(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")

    query, err := db.Prepare("delete from posts where id=?")
    catch(err)
    _, er := query.Exec(id)
    catch(er)
    query.Close()

    respondwithJSON(w, http.StatusOK, map[string]string{"message": "successfully deleted"})
}

要捕获 ID,我使用 chi.URLParam。 在你尝试实现 AllPostDetailPost 方法遇到困难时可以参考我的 Github Repo,这里面包含完整的代码。

现在是时候编写主要功能和一些辅助功能了。

go-sql-7.go

func main() {
    routers()
    http.ListenAndServe(":8005", Logger())
}

运行这个示例将在端口 8005 上启动一个服务器,可以在 http://localhost:8005 上访问它。(日志功能在你的终端上打印你每个请求的日志,它是在 helper.go 文件内实现的。)

我们将 main.go 拆分一部分为 helpler.go,因为它包括了我们的 helper 函数。你也可以将路由拆分出去,并将路由功能迁移到 router.go。

helper.go

package main

import ( 
    "encoding/json" 
    "fmt" 
    "net/http" 
    "time"
)

func catch(err error) {
    if err != nil {
        panic(err)
    }
}

// respondwithError 返回错误消息
func respondWithError(w http.ResponseWriter, code int, msg string) {
    respondwithJSON(w, code, map[string]string{"message": msg})
}

// respondwithJSON 编写 json 响应格式
func respondwithJSON(w http.ResponseWriter, code int, payload interface{}) {
    response, _ := json.Marshal(payload)
    fmt.Println(payload)
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(code)
    w.Write(response)
}

// Logger 返回日志信息
func Logger() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Println(time.Now(), r.Method, r.URL)
        router.ServeHTTP(w, r) // 分发请求
    })
}

我认为你已经理解了上面的代码。

好的,我们现在可以运行我们的代码了吗?在我的 github 仓库中包含了一个 makefile ,它可以帮助我们保存我们每次构建应用程序的时间。

如果在你的代码目录中包含 makefile,请运行

make run

如果你不这样做,则会构建 main.go

go build .

它将会在你的目录中创建一个二进制文件,并运行 $./wiki 命令,因为我们的目录名为 wiki。目前为止,我们的应用程序正在 8005 端口上运行。

要测试 API,请打开 postman 并运行这些路由。例如 ——

很好!我们已经完成了这个任务。同样,你可以从 github repo 找到完整的源代码。我还在那里添加了 docker 和 docker-compose 文件。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://itnext.io/building-restful-web-a...

译文地址:https://learnku.com/go/t/39370

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!