跳转到主要内容

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

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

Golang 的历史


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

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

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

 

Go-jetbrain

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

Jetbrain-survey

该调查旨在绘制开发者社区的地图。 查看调查以了解有关 2021 年调查报告的更多信息。

此外,在堆栈溢出所做的年度调查中,Go 是开发人员最想学习的语言之一。

Stack-overflow-survey

开始使用 Go


要开始使用 Go,您需要在计算机上安装 Go 运行时。

安装完成后,在终端运行 go version 命令,会打印出安装在电脑中的 go version。这样,您将知道 Go 已正确安装。

关于 Go 的一件事是,它对您如何安排和设置项目文件夹和文件结构非常固执己见。这意味着您需要很好地安排目录层次结构以调整 Go 代码。因此,请查看本指南并了解如何安排您的 Go 工作流程。

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

golang-vscodegolang-vscode

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

  • 创建应用程序时要做的第一件事就是定义一个包。所有的 go 文件都是在一个包中创建的。默认情况下,包始终是主包。因此,每个文件都会有它的包,它充当该文件的模块并且可以被导入。
  • 接下来是导入模块(这可能是本地模块或 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 <文件名或嵌套文件夹的文件路径>。

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


在服务器端使用 Golang 为 Web 应用程序创建后端。因此,我们可以使用它来创建基于 Web 的 API。那么让我们看看如何使用 Golang 和 PostgreSQL 数据库构建 REST API 应用程序。

安装 PostgreSQL 数据库设置


由于我们将使用 PostgreSQL 作为我们的数据库,因此请下载 PostgreSQL 并将其安装到您的本地计算机中。

然后,我们将创建一个数据库,向其中添加一个表和一些记录。以下是一些示例查询。首先,在您的 PostgreSQL pgAdmin 中创建一个电影数据库。

要创建表,请使用以下查询:

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 文件定义一个包。由于这将是我们拥有的主文件,因此我们添加包 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 数据的核心包。
  • database/sql - 用于处理基于 SQL 的数据库通信的核心包。
  • 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 数据库连接参数


为了与 PostgreSQL 等 SQL 数据库进行通信,我们需要添加数据库参数来帮助我们访问和操作数据。这包括数据库用户(默认情况下,这应该是 `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 语言中的类。

例如,struct 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 主函数


当 main 函数执行时,我们还需要运行一些端点来帮助我们通过 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。这将成功启动应用程序并为其提供服务,以便我们可以访问它。

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

使用 Golang 的一些优点和缺点

 

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


结论


Go 是一种奇妙而优雅的语言。 Go 旨在提高 C 等低级语言的效率。Go 最突出的是它利用多核处理的能力,这确保了低内存使用和低 GPU 功率。

快乐编码!

文章链接