如何使用Golang和PostreSQL建立一个REST-API

181 阅读10分钟

如何使用Golang和PostreSQL构建REST-API

Golang是一种开源的编程语言,它使人们能够轻松地构建简单、可靠和高效的应用程序。它是一种新兴的语言,开发了Docker和Kubernetes等技术。Go是一种编译的语言。因此,当人们写一个程序时,它会被编译并生成该程序的机器码等价物。

本教程将讨论Golang并帮助我们使用Golang和PostgreSQL数据库创建一个REST API应用程序。

Golang的历史

Go是由谷歌开发的。Go通常被称为Golang。它被称为Golang是由于它的域名golang.org ,原因是go.org ,无法使用。因此Go语言被简称为Golang,因此域名为golang.org

Golang的开发项目开始于2007年。其主要目的是利用多核处理器来开发大型分布式系统和高度可扩展的网络服务器。

Golang项目在2009年被开放源代码,其第一个初始版本1.0在2012年发布。谷歌Jetbrain团队将Go称为最有前途的编程语言。

Go-jetbrain

根据JetBrains 2021年的开发者生态系统调查,Go被列为首批语言之一。它也是开发者计划采用或迁移到的顶级编程语言之一。

Jetbrain-survey

该调查的目的是绘制开发者社区的景观。

另外,在stack overflow所做的年度调查中,Go是开发者最想学习的语言之一。

Stack-overflow-survey

开始学习Go

要开始使用Go,你需要在你的电脑上安装Go运行时间。

一旦安装完毕,在你的终端运行go version 命令,你电脑中安装的Go版本就会被打印出来。这样,你就会知道Go已经正确安装了。

关于Go的一件事是,它对你如何安排和设置项目的文件夹和文件结构非常有意见。这意味着你需要很好地安排你的目录层次,以调整Go代码。

在这里,我们将使用Visual studio代码与Go互动。所以下载并安装它。之后,打开Visual studio code并下载Go扩展。这将帮助我们编写和执行Go,以及为Go代码提供IntelliSense。

golang-vscode

我们将创建一个简单的Go应用程序(Hello world! ),看看Go是如何工作的。因此,创建一个项目文件夹,用VisualStudio代码打开它,并创建一个index.go 文件。

  • 当我们创建一个应用程序时,首先要做的是定义一个包。所有的Go文件都是在一个包内创建的。默认情况下,包总是package main 。因此,每个文件都会有它的包,它作为该文件的模块,可以被导入。

  • 接下来就是导入模块(这可能是本地模块或Go模块)。在这种情况下,我们将使用fmt 模块。fmt 模块允许你向控制台打印信息和文本。

  • 你需要为你的应用程序提供一个入口点。这是一个帮助你执行代码的Go初级函数。当你构建和运行Go程序时,该函数将被自动调用。

现在当你编写hello world程序时,你将拥有以下基本属性。

// Go package
package main

/// Go fmt import
import "fmt"

// Go main function
func main() {
    fmt.Println("Hello World!")
}

导航到项目目录来运行代码,然后在你喜欢的终端执行命令go run <file name or file path for nested folders>

使用Golang和PostgreSQL数据库构建一个REST API应用程序

Golang用于服务器端,为网络应用程序创建一个后端。因此,我们可以用它来创建基于网络的API。所以让我们看看如何使用Golang和PostgreSQL数据库建立一个REST API应用。

安装PostgreSQL数据库设置

由于我们将使用PostgreSQL作为我们的数据库,请下载并安装[PostgreSQL]到你的本地计算机。

然后,我们将创建一个数据库,向其中添加一个表和一些记录。下面是一些查询样本。首先,在你的PostgreSQL pgAdmin中创建一个movies 数据库。

要创建一个表,使用下面的查询。

CREATE TABLE movies (
    id SERIAL,
    movieID varchar(50) NOT NULL,
    movieName varchar(50) NOT NULL,
    PRIMARY KEY (id)
)

要向表中添加一些数据,请使用。

INSERT INTO movies (
    movieID,
    movieName
)
VALUES
    ('1', 'movie3'),
    ('2 ', 'movie2'),
    ('3', 'movie1');

添加软件包main

在你的项目目录的根部创建一个文件,并命名为index.go 。正如我们所说,我们需要为Go文件定义一个包。由于这将是我们的主文件,我们添加package main

package main

导入必要的包

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    _ "github.com/lib/pq"
)
  • fmt 和 - 用于记录错误和打印消息log
  • encoding/json - 用于处理JSON数据的Go核心包。
  • database/sql - 用于处理基于SQL的数据库通信的Go核心包。
  • net/http - 一个Go HTTP包,用于在创建GO API时处理HTTP请求
  • [mux](github.com/gorilla/mux) - 用于URL匹配器和路由。它有助于实现请求路由器,并将每个传入的请求与它的匹配处理程序相匹配。
  • [pq](github.com/lib/pq) - 一个用于处理数据库/SQL包的Go PostgreSQL驱动。

这里我们有两个第三方的库。为了使用它们,我们需要安装它们,以便我们的应用程序可以访问和使用它们。

首先,运行go mod init example.com/m 。这将生成一个go.mod 文件,保存我们需要的第三方库。要安装mux ,请运行go get github.com/gorilla/mux 。要安装pq ,请运行go get github.com/lib/pq

添加PostgreSQL数据库连接参数

为了与SQL数据库(如PostgreSQL)通信,我们需要添加数据库参数以帮助我们访问和处理数据。这包括数据库用户(默认情况下,这应该是`Postgres'),安装PostgreSQL时设置的密码,以及你想使用的数据库名称。

const (
    DB_USER     = "postgres"
    DB_PASSWORD = "12345678"
    DB_NAME     = "movies"
)

// DB set up
func setupDB() *sql.DB {
    dbinfo := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", DB_USER, DB_PASSWORD, DB_NAME)
    db, err := sql.Open("postgres", dbinfo)

    checkErr(err)

    return DB
}

添加JSON结构

一个结构就像一个类。它用于 Golang 中的面向对象编程,可以有属性和方法。结构的工作原理类似于 JavaScript 中的 ES6 类以及 Java 和 C sharp 中的类。

例如,结构Movie 将定义我们要获取的JSON字段。一旦获取数据,结构JsonResponse 将显示JSON响应。

type Movie struct {
    MovieID   string `json:"movieid"`
    MovieName string `json:"moviename"`
}

type JsonResponse struct {
    Type    string `json:"type"`
    Data    []Movie `json:"data"`
    Message string `json:"message"`
}

添加Go主函数

当主函数被执行时,我们还需要运行一些端点,这些端点将帮助我们通过HTTP请求进入服务器。这里,每个端点将执行一个方法。当该端点被调用时,该方法将通过调用定义了特定方法的必要参数的函数来执行。

因此,首先,初始化mux 路由器,然后添加路由器处理程序,为我们的API建立端点。然后,添加一个端口来为应用程序服务。

// Main function
func main() {

    // Init the mux router
    router := mux.NewRouter()

// Route handles & endpoints

    // Get all movies
    router.HandleFunc("/movies/", GetMovies).Methods("GET")

    // Create a movie
    router.HandleFunc("/movies/", CreateMovie).Methods("POST")

    // Delete a specific movie by the movieID
    router.HandleFunc("/movies/{movieid}", DeleteMovie).Methods("DELETE")

    // Delete all movies
    router.HandleFunc("/movies/", DeleteMovies).Methods("DELETE")

    // serve the app
    fmt.Println("Server at 8080")
    log.Fatal(http.ListenAndServe(":8000", router))
}

处理消息的函数

当打印消息时,我们将添加一个函数,它将帮助我们处理要记录的消息,这取决于我们将与应用程序的哪一部分进行交互。

// Function for handling messages
func printMessage(message string) {
    fmt.Println("")
    fmt.Println(message)
    fmt.Println("")
}

处理错误的函数

为了跟踪执行应用程序时可能发生的任何错误,我们将添加一个函数来检查并记录该错误。

// Function for handling errors
func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

取出所有记录

// Get all movies

// response and request handlers
func GetMovies(w http.ResponseWriter, r *http.Request) {
    db := setupDB()

    printMessage("Getting movies...")

    // Get all movies from movies table that don't have movieID = "1"
    rows, err := db.Query("SELECT * FROM movies")

    // check errors
    checkErr(err)

    // var response []JsonResponse
    var movies []Movie

    // Foreach movie
    for rows.Next() {
        var id int
        var movieID string
        var movieName string

        err = rows.Scan(&id, &movieID, &movieName)

        // check errors
        checkErr(err)

        movies = append(movies, Movie{MovieID: movieID, MovieName: movieName})
    }

    var response = JsonResponse{Type: "success", Data: movies}

    json.NewEncoder(w).Encode(response)
}

在数据库中插入一条记录

// Create a movie

// response and request handlers
func CreateMovie(w http.ResponseWriter, r *http.Request) {
    movieID := r.FormValue("movieid")
    movieName := r.FormValue("moviename")

    var response = JsonResponse{}

    if movieID == "" || movieName == "" {
        response = JsonResponse{Type: "error", Message: "You are missing movieID or movieName parameter."}
    } else {
        db := setupDB()

        printMessage("Inserting movie into DB")

        fmt.Println("Inserting new movie with ID: " + movieID + " and name: " + movieName)

        var lastInsertID int
    err := db.QueryRow("INSERT INTO movies(movieID, movieName) VALUES($1, $2) returning id;", movieID, movieName).Scan(&lastInsertID)

    // check errors
    checkErr(err)

    response = JsonResponse{Type: "success", Message: "The movie has been inserted successfully!"}
    }

    json.NewEncoder(w).Encode(response)
}

删除一条记录

// Delete a movie

// response and request handlers
func DeleteMovie(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)

    movieID := params["movieid"]

    var response = JsonResponse{}

    if movieID == "" {
        response = JsonResponse{Type: "error", Message: "You are missing movieID parameter."}
    } else {
        db := setupDB()

        printMessage("Deleting movie from DB")

        _, err := db.Exec("DELETE FROM movies where movieID = $1", movieID)

        // check errors
        checkErr(err)

        response = JsonResponse{Type: "success", Message: "The movie has been deleted successfully!"}
    }

    json.NewEncoder(w).Encode(response)
}

删除所有记录

// Delete all movies

// response and request handlers
func DeleteMovies(w http.ResponseWriter, r *http.Request) {
    db := setupDB()

    printMessage("Deleting all movies...")

    _, err := db.Exec("DELETE FROM movies")

    // check errors
    checkErr(err)

    printMessage("All movies have been deleted successfully!")

    var response = JsonResponse{Type: "success", Message: "All movies have been deleted successfully!"}

    json.NewEncoder(w).Encode(response)
}

测试

完成上述工作后,我们现在可以运行和测试这个应用程序。要运行它,打开终端,将目录改为项目目录,然后运行go run index.go 。这将成功地启动该应用程序,并为其提供服务,以便我们可以访问它。

要开始测试,使用postman并开始向各自的端点发送请求。

使用Golang的一些优点和缺点

  • Go是为兼容大型企业而生的。它有能力处理大量的数据处理。因此,如果应用程序要处理的数据量逐渐增加,它是一个不错的选择。
  • Go处理数据的延时很低。这意味着你不必担心从用户请求一些数据到这些数据被提供给用户之间的响应时间。
  • 完善的并发性和多线程支持。Go的并发性是内建的;因此,编译和执行的速度更快。
  • Go非常注重应用程序的效率。它是非常简约的。它有一个小的代码库,没有泛型模板和独立的运行时库。因此,在编译时,二进制文件变得更小,使得Golang的编写、编译和执行速度很快。
  • 学习曲线。Go是低级语言(如C)和现代语言(如Python)的结合。这使得如果你以前学过其他语言,就会更容易学习。另外,像Typescipt一样,Go支持结构化和静态类型化。你可以在编译前发现错误;因此,基础知识可以很快学会,使其学习更快。
  • 它没有GUI库。因此你需要连接库,而不是使用Python或java等语言中的本地解决方案。
  • Go仍然是一种年轻的语言。Go没有许多其他成熟语言如Java和Python的成熟度和用户经验。然而,它们有大量的内置代码和许多由不同的开源社区开发者不断创建和维护的库。它仍然年轻的事实意味着Go程序员的就业市场还不广泛。

总结

Go是一种神奇且相当优雅的语言。Go的目标是提高C语言等低级语言的效率。Go最突出的是它利用多核处理的能力,这确保了低内存使用率和低GPU功耗。