如何使用GO构建API
#编程 #api #go #technicalwriting

您是否曾经觉得构建API就像创建一个秘密代码,只有少数人能理解?作为Web开发人员 - ¾ââ»和TechWriterð,我去过那里。但是不要害怕,我的朋友!在本文中,我将向您展示GO如何帮助您解锁API开发的奥秘并使其任何人都可以使用。
因此,让我们深入了解API的世界,然后发现如何轻松创建强大而有效的API。

解释什么是API以及为什么有用

API是一组用于构建软件应用程序的协议,例程和代码。 API指定软件组件应如何相互作用和通信,从而使开发人员更容易构建复杂的软件系统。 API为软件提供了交换数据​​和执行功能的标准化方式,使开发人员更容易创建新的应用程序,集成现有系统并自动化流程。

API很有用,因为它们允许开发人员创建可以与其他软件系统,服务和数据源进行交互的应用程序。这意味着开发人员不必每次都想构建新应用程序时从头开始,他们可以利用现有的代码,数据和功能来创建新的和创新的产品。 API还使开发人员能够构建可扩展,模块化和可维护的软件产品,从而更容易随着时间的推移添加新功能和功能。

简要说明GO及其对建筑API的好处

GoGoogle开发的一种现代编程语言,它在开发人员的构建API中广受欢迎。 Go是一种编译的语言,这意味着它可以生成可以在各种平台上运行的快速有效的代码。 GO还设计为易于读写,使其成为构建大型和复杂软件系统的绝佳选择。

使用GO构建API的关键好处之一是其对并发的绝佳支持。并发是能够同时运行多个任务的能力,并且GO可以轻松编写可以利用多核处理器和其他硬件资源的代码。这意味着GO API可以处理高水平的流量和请求而不会减慢或崩溃。

将GO用于构建API的另一个好处是其对JSON(JavaScript对象符号)的内置支持,这是一种轻巧的数据格式,在We​​b应用程序中广泛用于数据交换。 GO对JSON的支持使创建可以以这种格式接受和返回数据的API易于简化构建和集成API与其他软件系统的过程。

总的来说,GO是一种强大而灵活的语言,非常适合构建API。它的速度,并发支持和内置的JSON支持使其成为希望创建可扩展,高性能API的开发人员。

设置GO环境

  • 安装GO

第一步是下载并安装在计算机上。您可以从官方Go website下载最新版本的GO。下载后,按照操作系统的安装说明。

  • 配置环境变量

安装GO后,您需要配置环境变量。在Windows中,您可以通过右键单击“我的计算机”并选择“属性”来执行此操作。从那里,单击“高级系统设置”,然后单击“环境变量”。将路径添加到GO BIN文件夹中的“路径”变量。

在Linux和MacOS中,您需要在主目录中编辑.bashrc.bash_profile文件,并添加以下行:

export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

  • 设置工作区

接下来,您需要为您的GO项目设置工作区。工作区是包含您的GO源代码文件和二进制文件的目录。

在主目录中创建一个名为go-workspace的目录:

$ mkdir ~/go-workspace

在此目录中,创建三个子目录:srcpkgbin

$ cd ~/go-workspace
$ mkdir src pkg bin

您准备开始使用GO构建API!

构建API

  • 选择一个框架(例如Gorilla Mux,Echo,Gin)

现在我们已经设置了GO环境,我们可以开始构建API。第一步是选择一个框架。有几个流行的框架,用于在GO中构建API,例如Gorilla muxEchoGin。在本文中,我们将使用Gorilla mux

  • 创建基本服务器

要创建基本服务器,我们需要导入必要的软件包并定义主要功能。主要功能是我们程序的入口点。这是使用大猩猩Mux的基本服务器的示例:

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter()

    router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Hello, World!")
    })

    http.ListenAndServe(":8000", router)
}

此代码导入“ FMT”,“ Net/http”和“ github.com/gorilla/mux”软件包,定义了使用Gorilla Mux创建新路由器的主函数,为“/// “路线写着“你好,世界!”向响应作者,并启动了在端口8000上听的HTTP服务器。

  • 添加不同端点的路由和处理程序

要为不同端点添加路由和处理程序,我们需要定义其他处理程序功能并将其注册为路由器。

// Define a handler function for the /users endpoint
func getUsersHandler(w http.ResponseWriter, r *http.Request) {
  // Get the list of users from the database
  users := getUsersFromDB()

  // Convert the list of users to JSON format
  usersJSON, err := json.Marshal(users)
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }

  // Set the content type of the response to JSON
  w.Header().Set("Content-Type", "application/json")

  // Write the JSON response to the client
  w.Write(usersJSON)
}

// Register the /users endpoint with the router
r.HandleFunc("/users", getUsersHandler).Methods(http.MethodGet)

在此示例中,我们定义了一个getUsersHandler函数,该功能从数据库中检索用户列表,将其转换为JSON格式,并将其写入响应。然后,我们通过调用r.HandleFunc("/users", getUsersHandler).Methods(http.MethodGet)注册该处理程序功能。

我们可以类似地为其他端点定义和注册处理程序功能,例如用于通过ID检索特定用户的/users/{id},使用HTTP POST方法来创建新用户的/users,使用HTTP PUT方法来更新用户,以及使用/users/{id}/users/{id},以及HTTP删除用于删除用户的方法。

  • 实施CRUD操作:

要实现CRUD操作(创建,读取,更新,删除),我们需要为每个操作定义处理程序功能并向路由器注册。

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "github.com/gorilla/mux"
)

type Book struct {
    ID        string   `json:"id,omitempty"`
    Title     string   `json:"title,omitempty"`
    Author    string   `json:"author,omitempty"`
    Publisher *Company `json:"publisher,omitempty"`
}

type Company struct {
    Name string `json:"name,omitempty"`
    Address string `json:"address,omitempty"`
}

var books []Book

func GetBooks(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(books)
}

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

func CreateBook(w http.ResponseWriter, r *http.Request) {
    var book Book
    _ = json.NewDecoder(r.Body).Decode(&book)
    books = append(books, book)
    json.NewEncoder(w).Encode(book)
}

func UpdateBook(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    for index, item := range books {
        if item.ID == params["id"] {
            books = append(books[:index], books[index+1:]...)
            var book Book
            _ = json.NewDecoder(r.Body).Decode(&book)
            book.ID = params["id"]
            books = append(books, book)
            json.NewEncoder(w).Encode(book)
            return
        }
    }
    json.NewEncoder(w).Encode(books)
}

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

func main() {
    router := mux.NewRouter()

    books = append(books, Book{ID: "1", Title: "Book One", Author: "John Doe", Publisher: &Company{Name: "Publisher One", Address: "Address One"}})
    books = append(books, Book{ID: "2", Title: "Book Two", Author: "Jane Smith", Publisher: &Company{Name: "Publisher Two", Address: "Address Two"}})

    router.HandleFunc("/books", GetBooks).Methods("GET")
    router.HandleFunc("/books/{id}", GetBook).Methods("GET")
    router.HandleFunc("/books", CreateBook).Methods("POST")
    router.HandleFunc("/books/{id}", UpdateBook).Methods("PUT")
    router.HandleFunc("/books/{id}", DeleteBook).Methods("DELETE")

    log.Fatal(http.ListenAndServe(":8000", router))
}

此代码使用IDTitleAuthorPublisher字段定义了Book结构。然后,它定义了处理程序的功能,以获取所有书籍,通过ID获取特定书籍,创建新书,更新现有书籍并删除书籍。这些处理程序功能使用Gorilla Mux的Vars函数从URL提取变量,并且它们使用json软件包来编码和解码JSON数据。最后,设置路由器并使用适当的HTTP方法和URL路径注册处理程序功能,从而使API接收并响应请求。这会创建一个功能齐全的恢复API,可以将其部署到服务器上,并由客户用来对书籍集合进行CRUD操作。
在此示例中,您可以使用GO和Gorilla Mux开始建立自己的API,并自定义以满足您的特定需求。

处理错误和例外

处理错误和异常是构建任何软件的重要方面,API也不例外。构建API时,重要的是要预测和处理运行时可能发生的错误和例外。
某些用于处理错误和GO API例外的技术是:

  • 处理程序功能中的错误处理:

处理错误API中错误和异常的一种方法是在处理程序功能中使用错误处理。

func getUser(w http.ResponseWriter, r *http.Request) {
    // ...code to get user by ID...
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    // ...return user as JSON...
}

如果在获取用户时发生错误,则将作为HTTP 500内部服务器错误返回。

  • 自定义错误类型:

在GO中,您可以定义自定义错误类型以表示API中可能发生的特定错误。

type UserNotFoundError struct {
    ID int
}

func (e UserNotFoundError) Error() string {
    return fmt.Sprintf("user with ID %d not found", e.ID)
}

当找不到具有给定ID的用户时,定义了自定义错误类型UserNotFoundError表示错误。它可以在处理程序功能中使用:

func getUser(w http.ResponseWriter, r *http.Request) {
    // ...code to get user by ID...
    if err != nil {
        if _, ok := err.(UserNotFoundError); ok {
            http.Error(w, err.Error(), http.StatusNotFound)
            return
        }
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    // ...return user as JSON...
}

如果发生了UserNotFoundError类型的错误,将其返回为HTTP 404。

  • 恐慌和恢复:

处理错误API中错误和异常的另一种方法是使用恐慌和恢复机制。

func getUser(w http.ResponseWriter, r *http.Request) {
    defer func() {
        if r := recover(); r != nil {
            log.Println("recovered from panic:", r)
            http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        }
    }()
    // ...code that may panic...
}

如果处理程序功能中发生恐慌,将被捕获并记录下来,并将http 500内部服务器错误返回客户端。

通过实施这些技术,您可以确保您的GO API强大,并且可以优雅地处理错误和异常。

与数据库集成

与数据库集成是构建准备生产的API的关键步骤。有许多数据库可供选择,例如MySQLPostgreSQLMongoDB等。在本节中,我们将介绍与数据库集成的步骤。

  • 选择数据库(例如MySQL,PostgreSQL,MongoDB)

数据库的选择取决于项目的特定需求。例如,如果应用程序需要高度关系数据模型,则基于SQL的数据库(例如MySQL或PostgreSQL)可能是最佳选择。另一方面,如果数据更面向文档或非结构化,则NOSQL数据库(例如MongoDB)可能更合适。选择适合项目需求的数据库,同时考虑诸如性能,可伸缩性和成本等因素。

  • 安装必要的驱动程序

要与特定数据库集成,我们需要安装适当的驱动程序或软件包。例如,要使用MySQL,我们可以安装“ GO-SQL-DRIVER/MYSQL”软件包。同样,我们可以使用“ PQ”软件包连接到PostgreSQL,或者可以连接到MongoDB的“ Mongo-go-driver”软件包。

  • 创建数据库连接

安装了必要的软件包后,我们可以创建一个数据库连接。连接字符串通常包含连接到数据库所需的凭据,例如用户名,密码和数据库名称。

创建与MySQL数据库的连接:

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

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(host:port)/database")
    if err != nil {
        // handle error
    }
    defer db.Close()

    // do something with db
}

  • 实施数据库操作(例如,查询,插入,更新,删除)

数据库操作(例如查询,插入,更新和删除数据)可以在连接后执行。

从MySQL数据库中检索所有用户的功能:

func getUsers(db *sql.DB) ([]User, error) {
    var users []User
    rows, err := db.Query("SELECT * FROM users")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    for rows.Next() {
        var user User
        err := rows.Scan(&user.ID, &user.Name, &user.Email)
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }

    return users, nil
}

将GO与数据库集成是构建准备生产的API的关键步骤。
选择适当的数据库,安装必要的驱动程序,创建连接并实现数据库操作。

增加身份验证和授权

将身份验证和授权添加到API中对于确保安全访问和控制敏感数据很重要。这是解决方法:

  • 选择一种身份验证和授权方法(例如JWT,OAUTHZ)

首先,选择适合API需求的身份验证和授权方法。常见方法包括JWT(JSON Web令牌),OAUTH2和基本身份验证。

  • 实施身份验证和授权中间件

接下来,您需要实现中间件功能来处理身份验证和授权。这些功能应检查有效的身份验证凭据,并根据用户的角色和权限授权访问端点。

基本的JWT身份验证中间件功能:

func RequireTokenAuthentication(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "Missing authorization header", http.StatusUnauthorized)
            return
        }
        token, err := jwt.Parse(authHeader, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
            }
            return []byte("secret"), nil
        })
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }
        if !token.Valid {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }
        context.Set(r, "decoded", token.Claims)
        next.ServeHTTP(w, r)
    })
}

  • 将身份验证和授权添加到端点

实现了中间件后,我们可以将其添加到所需的端点。例如,要限制对具有特定角色的认证用户的访问,我们可以使用RequireTokenAuthentication middleware并添加角色检查:

router.HandleFunc("/protected", RequireTokenAuthentication(ProtectedHandler)).Methods("GET")

测试API

要确保API正常运行,我们需要编写测试。务必进行彻底测试,以确保其按预期工作,并在与用户联系之前捕获任何错误或问题。
可以将测试分为单个处理程序和端点的单位测试,以及整个API的集成测试。 GoConvey和Ginkgo等测试框架可以帮助编写和运行测试。

  • 编写终点和处理程序的单元测试。

单元测试的重点是测试代码的单个功能或组件。对于我们的API,我们需要为每个端点和处理程序功能编写单元测试,以确保它们正常工作。

这是处理程序的测试功能:

func TestProtectedHandler(t *testing.T) {
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "username": "testuser",
        "role": "admin",
        "exp": time.Now().Add(time.Hour * 24).Unix(),
    })
    tokenString, err := token.SignedString([]byte("secret"))
    if err != nil {
        t.Fatalf("Error signing token: %v", err)
    }
    req, err := http.NewRequest("GET", "/protected", nil)
    if err != nil {
        t.Fatalf("Error creating request: %v", err)
    }
    req.Header.Set("Authorization", "Bearer "+tokenString)
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(ProtectedHandler)
    handler.ServeHTTP(rr, req)
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("Handler returned wrong status code: got %v want %v", status, http.StatusOK)
    }
    expected := `{"message":"Hello, authenticated user!"}`
    if rr.Body.String() != expected {
        t.Errorf("Handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
    }
}

  • 编写API的集成测试:
另一方面,

集成测试测试了API,以确保所有组件都正确地工作。
使用银杏测试框架的集成测试:

var _ = Describe("API", func() {
    var (
        apiURL string
        api *API
    )

    BeforeEach(func() {
        apiURL = "http://localhost:8080"
        api = NewAPI()
        go api.Start()
        time.Sleep(1 * time.Second)
    })

    AfterEach(func() {
        api.Stop()
    })

    Describe("GET /users", func() {
        It("returns a list of users", func() {
            resp, err := http.Get(fmt.Sprintf("%s/users", apiURL))
            Expect(err).NotTo(HaveOccurred())
            defer resp.Body.Close()

            Expect(resp.StatusCode).To(Equal(http.StatusOK))

            bodyBytes, err := ioutil.ReadAll(resp.Body)
            Expect(err).NotTo(HaveOccurred())
            bodyString := string(bodyBytes)

            Expect(bodyString).To(ContainSubstring("John Doe"))
            Expect(bodyString).To(ContainSubstring("johndoe@example.com"))
        })
    })
})

测试是构建强大的API的重要组成部分,使用GoConvey或Ginkgo等测试框架可以使过程更容易,更有效。

部署API

太好了!开发,测试并准备好生产的API后,我们需要部署它,以便我们的客户可以访问它。根据您的项目要求和基础架构功能,有各种各样的部署方法可供选择。 (例如,Docker,Heroku,AWS)

我们将通过在Heroku上部署API的步骤,这是一个流行的服务(PAAS)提供商。

  • 创建一个Heroku帐户:
    首先,如果您还没有一个帐户,请创建一个Heroku帐户。访问Heroku website并注册免费帐户。

  • 安装Heroku CLI
    接下来,通过按照Heroku网站上的说明下载并安装Heroku CLI。这个CLI将使我们能够从终端与Heroku互动。

  • 创建一个新的Heroku应用程序:
    安装了Heroku CLI后,通过在终端中运行以下命令来创建一个新的Heroku应用:

heroku create <app-name>

用您要给应用的名称替换<app-name>。这将在Heroku上创建一个新应用,并为您提供访问它的URL。

  • 设置环境变量: 如果您的API使用任何环境变量,例如数据库连接字符串或身份验证键,则需要在Heroku上进行设置。 您可以通过在终端中运行以下命令来执行此操作:
heroku config:set <KEY>=<VALUE>

用环境变量的名称替换<KEY>,而<VALUE>则具有其价值。

  • 将您的代码提交给git:
    在我们可以在Heroku上部署应用程序之前,我们需要将代码提交给GIT。确保您的代码位于git存储库中并提交您所做的任何更改。

  • 部署您的应用程序:
    当您的代码致力于Git时,您可以通过在终端中运行以下命令将应用程序部署到Heroku:

git push heroku master

这将使您的代码将您的代码推向Heroku并触发构建过程。构建完成后,您的应用程序将在Heroku上启动并运行。

  • 缩放您的应用程序: 如果您拥有高流量应用程序,则可以通过在终端中运行以下命令来扩展它:
heroku ps:scale web=<number-of-instances>

用要运行的实例替换<number-of-instances>

恭喜!您已成功地将API部署在Heroku上。您现在可以使用Heroku提供的URL访问它。

结论

在本教程中,我们涵盖了GO构建API的基础知识。我们首先了解什么是API以及它们为何有用。然后,我们继续探索使用GO构建API并建立我们的开发环境的好处。

我们了解了选择一个框架并创建基本服务器,为不同端点添加路由和处理程序,实现CRUD操作,处理错误和异常,与数据库集成以及添加身份验证和授权。最后,我们介绍了测试API并使用Docker部署。

尽管我们在本教程中介绍了很多东西,但还有更多关于使用GO构建API的知识。要继续您的学习旅程,请查看下面列出的其他资源:

感谢您的阅读,希望您对此教程有所帮助。如果您有任何疑问或评论,请随时在LinkedInTwitter上与我建立联系。如果您真的很喜欢这个教程,则可以buy me a coffee

Freepik的图像