如何用Golang的原生模块建立一个REST API

489 阅读9分钟

如何使用本地模块用Golang构建REST API

Go是一种在开发者中快速增长的语言。Golang生态系统有大量的本地库。然而,它也有第三方库,开发者可以用它来构建API。

在用Go创建REST API时,你可以选择使用各种第三方库,如Gorm、Go fiber、Gin、fast HTTP等。

开发人员也可以使用Golang的本地库。这些库在构建API时不需要你把它们下载到你的本地项目中。

在本指南中,我们将使用Golang的本地库构建一个基本的REST API。我们将不使用任何第三方库,也就是说,我们不会在本地项目中下载任何库。

先决条件

要跟上本教程,读者需要。

  • 一些Golang的基本知识。
  • 在你的电脑上安装一个IDE(最好是VS Code)。
  • 在你的电脑上安装Go

一旦你安装了Go,运行下面的命令来验证Go的版本。

go version

开始了解如何编写和运行 Golang 程序。

设置一个Golang项目

要启动一个Go项目,首先需要在项目中初始化Go。

在你希望你的应用程序所在的地方创建一个项目文件夹。打开指向新创建目录的命令行,然后使用以下命令初始化 Golang。

go mod init native-go-api

这将通过创建一个go.mod 文件来实例化Go。你所安装的任何Go软件包及其依赖项都会保存在这里。

go.mod 文件有一个native-go-api 模块名称。这可以帮助你创建本地模块并导入它们,这样你项目的其他本地模块就可以使用它们。

使用本地模块创建一个CRUD Golang REST API

让我们深入了解并使用本地模块创建一个 Golang REST API。在本教程中,我们将创建一个简单的电影API。这将有助于展示如何在一个典型的Golang应用程序中使用这些本地库。

首先,你需要对你希望你的API与之交互的数据进行建模。要做到这一点,在你项目的根目录下创建一个models 文件夹。

然后创建一个movie.go 文件并添加以下模型。

package models

// Movie for modeling data dummy
type Movie struct {
 ID      string `json:"id"`
 Title    string `json:"title"`
 Description string `json:"description"`
}

一个模型设定了你的API的一个蓝图。它设置了数据和它的值。这为API将使用的每个电影属性添加了数据类型。

当使用Go来创建一个模型时,使用关键字struct 。在这里,设置一个Movie 类型的struct ,并添加其属性,如上面的代码块所示。

这个API将与作为应用数据库的假数据进行交互。因此,在你的应用程序中创建一个假的数据库。

要做到这一点,在你的项目目录中创建一个文件夹db 。在这个db 文件夹中,创建一个db.go 文件并添加以下代码。

package db

import ("native-go-api/models")

// set up a database dummy
var (Moviedb = make(map[string]models.Movie))

这将建立一个假的数据库,Moviedb 。这将使用make 内置函数,它分配并初始化一个类型为map 的内存对象。

它还需要一个类型作为它的第一个参数,make 类型返回与它的参数类型相同的数据。在这里,make 将指为电影数据设置的Movie 模型的类型。

设置电影处理程序

一个API使用HTTP方法与你的数据进行交互。因此,你需要设置正确的函数来引用HTTP方法,如GET,POST,PUT, 和DELETE

这也将设置每个HTTP方法应该返回的请求和响应信息。

在这个例子中,API将始终以JSON格式返回任何数据。我们需要设置一个ReturnJsonResponse ,该函数将以JSON格式返回电影数据。

要做到这一点,在你的项目目录下创建一个utils 文件夹。在这个utils 文件夹中,创建一个utils.go 文件并添加以下代码。

package utils

import (
 "net/http"
)

// ReturnJsonResponse function for returning movies data in JSON format
func ReturnJsonResponse(res http.ResponseWriter, httpCode int, resMessage []byte) {
 res.Header().Set("Content-type", "application/json")
 res.WriteHeader(httpCode)
 res.Write(resMessage)
}

这将使用Go的本地http 模块。ReturnJsonResponse 函数将转换HTTP服务器返回的任何响应,并将Content-type 设置为JSON。

一旦设置完毕,就指定你的HTTP处理函数。首先,在你的项目目录中创建一个handler 文件夹。

在这个handler 文件夹中,创建一个movies.go 文件,开始导入Go的本地模块和上面步骤中创建的本地模块,如下图所示。

package handler

import (
 "native-go-api/models"
 "native-go-api/db"
 "native-go-api/utils"
 "net/http"
 "encoding/json"
)

这就导入了我们所创建的本地模块。其他使用的本地模块包括用于编码任何JSON数据的encoding/json 和用于设置基于服务器的方法、请求和响应的net/http

接下来,让我们创建handler ,函数如下。

创建一个API测试处理程序

设置一个测试处理程序,不与设定的API数据进行交互,以测试API是否正常工作。

// root api test handler
func TestHandler(res http.ResponseWriter, req *http.Request) {

 // Add the response return message
 HandlerMessage := []byte(`{
  "success": true,
  "message": "The server is running properly"
  }`)

 utils.ReturnJsonResponse(res, http.StatusOK, HandlerMessage)
}

当这个处理程序被执行时,它将返回设定的信息以显示服务器已经准备好了。

获取电影处理程序

为了获取虚拟数据库中的所有电影列表,创建一个GetMovies() ,如下图所示。

// Get Movies handler
func GetMovies(res http.ResponseWriter, req *http.Request) {

 if req.Method != "GET" {

  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Check your HTTP method: Invalid HTTP method executed",
  }`)

  utils.ReturnJsonResponse(res, http.StatusMethodNotAllowed, HandlerMessage)
  return
 }

 var movies []models.Movie

 for _, movie := range db.Moviedb {
  movies = append(movies, movie)
 }

 // parse the movie data into json format
 movieJSON, err := json.Marshal(&movies)
 if err != nil {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Error parsing the movie data",
  }`)

  utils.ReturnJsonResponse(res, http.StatusInternalServerError, HandlerMessage)
  return
 }

 utils.ReturnJsonResponse(res, http.StatusOK, movieJSON)
}

这个处理程序将访问数据库并检查是否有任何电影记录。服务器将获取这个列表并将响应返回给用户。

服务器将把电影数据解析成JSON格式,然后返回带有获取的数据的响应信息。

获取一个单一的电影处理程序

此外,你可以从电影列表数据库中获取一个单一的电影。在这种情况下,电影结构有一个ID ,每个电影都是独一无二的。

当获取单一电影时,服务器将使用ID 值作为参数来决定用户想要获取哪部电影。

// Get a single movie handler
func GetMovie(res http.ResponseWriter, req *http.Request) {

 if req.Method != "GET" {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Check your HTTP method: Invalid HTTP method executed",
  }`)

  utils.ReturnJsonResponse(res, http.StatusMethodNotAllowed, HandlerMessage)
  return
 }

 if _, ok := req.URL.Query()["id"]; !ok {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "This method requires the movie id",
  }`)

  utils.ReturnJsonResponse(res, http.StatusInternalServerError, HandlerMessage)
  return
 }

 id := req.URL.Query()["id"][0]

 movie, ok := db.Moviedb[id]
 if !ok {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Requested movie not found",
  }`)

  utils.ReturnJsonResponse(res, http.StatusNotFound, HandlerMessage)
  return
 }

 // parse the movie data into json format
 movieJSON, err := json.Marshal(&movie)
 if err != nil {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Error parsing the movie data",
  }`)

  utils.ReturnJsonResponse(res, http.StatusInternalServerError, HandlerMessage)
  return
 }

 utils.ReturnJsonResponse(res, http.StatusOK, movieJSON)
}

在这里,获取单一电影的端点将有id 作为URL参数。这个端点将只执行一个GET 的请求。

如果用户使用不同的方法,设置错误信息以显示他们应该做什么。如果用户忘记添加参数id ,添加响应返回信息。检查这个函数中实现的其他处理程序信息。

添加一个电影处理程序

要添加一个电影,使用下面的AddMovie() 函数执行POST方法。

// Add a movie handler
func AddMovie(res http.ResponseWriter, req *http.Request) {

 if req.Method != "POST" {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Check your HTTP method: Invalid HTTP method executed",
  }`)

  utils.ReturnJsonResponse(res, http.StatusMethodNotAllowed, HandlerMessage)
  return
 }

 var movie models.Movie

 payload := req.Body

 defer req.Body.Close()
 // parse the movie data into json format
 err := json.NewDecoder(payload).Decode(&movie)
 if err != nil {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Error parsing the movie data",
  }`)

  utils.ReturnJsonResponse(res, http.StatusInternalServerError, HandlerMessage)
  return
 }

 db.Moviedb[movie.ID] = movie
 // Add the response return message
 HandlerMessage := []byte(`{
  "success": true,
  "message": "Movie was successfully created",
  }`)

 utils.ReturnJsonResponse(res, http.StatusCreated, HandlerMessage)
}

这个函数将检查用户的有效载荷与用户想要添加的新数据。该服务将把这些数据分配给req.Body 。每个添加的电影将有所有的属性设置在Movie 模型中。

因此,如果用户未能指定这个数据字段,服务器将返回一个错误信息Error parsing the movie data

如果操作成功,服务器将返回一个服务器消息Movie was successfully created

删除一个电影处理程序

要删除一条电影记录,请将这个DeleteMovie() 函数添加到你的处理程序列表中。

// Delete a movie handler
func DeleteMovie(res http.ResponseWriter, req *http.Request) {

 if req.Method != "DELETE" {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Check your HTTP method: Invalid HTTP method executed",
  }`)

  utils.ReturnJsonResponse(res, http.StatusMethodNotAllowed, HandlerMessage)
  return
 }

 if _, ok := req.URL.Query()["id"]; !ok {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "This method requires the movie id",
  }`)

  utils.ReturnJsonResponse(res, http.StatusBadRequest, HandlerMessage)
  return
 }

 id := req.URL.Query()["id"][0]
 movie, ok := db.Moviedb[id]
 if !ok {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Requested movie not found",
  }`)

  utils.ReturnJsonResponse(res, http.StatusNotFound, HandlerMessage)
  return
 }
 // parse the movie data into json format
 movieJSON, err := json.Marshal(&movie)
 if err != nil {
  // Add the response return message
  HandlerMessage := []byte(`{
   "success": false,
   "message": "Error parsing the movie data"
  }`)

  utils.ReturnJsonResponse(res, http.StatusBadRequest, HandlerMessage)
  return
 }

 utils.ReturnJsonResponse(res, http.StatusOK, movieJSON)
}

DeleteMovie() 使用 ,添加到 的参数中。根据发送的请求,这将检查出服务器应该删除的现有电影。id URL

为了让服务器删除该电影,它将参数id与保存在虚拟数据中的id进行对照。如果没有找到该电影,服务器将返回一个响应信息Requested movie not found

初始化服务器和路由

为了执行上述处理程序,你需要。

  • 设置一个在localhost上运行的服务器。
  • 初始化数据库数据。
  • 设置指向每个处理程序函数的路由。

要做到这一点,在项目目录内创建一个main.go 文件。然后设置服务器。

首先,导入本地模块和Go的本地模块。

package main

import (
 "fmt"
 "log"
 "native-go-api/db"
 "native-go-api/handler"
 "native-go-api/models"
 "net/http"
 "os"
)

这里导入以下本地模块。

  • fmt - 用于设置 信息。Println
  • log - 用于记录基本的应用程序信息。
  • os - 用于访问本地计算机以在本地执行服务器。
  • net/http - 用于设置HTTP服务器和执行服务器路线。

接下来,创建一个main() ,如下图所示。

func main() {

}

在这个函数里面。

  • 添加一个打印信息,显示服务器正在运行。
log.Print("The is Server Running on localhost port 3000")
  • 初始化数据库并设置假的数据库数据。
// initialize the database
db.Moviedb["001"] = models.Movie{ID: "001", Title: "A Space Odyssey", Description: "Science fiction"}
db.Moviedb["002"] = models.Movie{ID: "002", Title: "Citizen Kane", Description: "Drama"}
db.Moviedb["003"] = models.Movie{ID: "003", Title: "Raiders of the Lost Ark", Description: "Action and adventure"}
db.Moviedb["004"] = models.Movie{ID: "004", Title: "66. The General", Description: "Comedy"}
  • 添加指向每个处理程序的服务器路由。
// route goes here

// test route
http.HandleFunc("/", handler.TestHandler)
// get movies
http.HandleFunc("movies", handler.GetMovies)
// get a single movie
http.HandleFunc("/movie", handler.GetMovie)
// Add movie
http.HandleFunc("/movie/add", handler.AddMovie)
// delete movie
http.HandleFunc("/movie/delete", handler.DeleteMovie)
  • 设置服务器端口并启动服务器。
// listen port
err := http.ListenAndServe(":3000", nil)
// print any server-based error messages
if err != nil {
 fmt.Println(err)
 os.Exit(1)
}

你的服务器应该已经准备好了。要测试它,请运行go run main.go ,并开始与你的API的不同路由进行交互。

总结

本教程教你如何使用本地Go模块并创建一个基本的电影API。这些原生模块帮助开发者与vanilla Golang代码进行交互。

一些你可以用来设置服务器的Golang第三方库包括,muxGo FiberGormEcho