1. 概述和要求
REST 是 Representational State Transfer 的缩写。可以使用 REST 来公开 Web 应用程序中的信息,并允许其他应用程序访问该信息。REST 本质上是一种可用于让 Web 应用程序访问资源以及另一个系统上资源状态信息的架构。通过使用 RESTful API,可以与其他服务器和系统进行交互。
下面将介绍如何创建一些有助于处理远程进程的元素。
- 请求:请求是 HTTP 请求。 API 内部的所有请求都是以 HTTP 请求的形式进行的。
- 处理程序:处理程序是一个处理客户端传入请求的函数。每种类型的请求都应该有一个适当的处理程序进行处理,并向该请求提供响应。
- 路由器:路由器是将一个 API 的端点映射到相应的处理程序的函数。
我们的目标是创建一个完整的 RESTful API,该 API 使用 GET、POST、PUT 和 DELETE 这 4 种 HTTP 请求来创建、读取、更新和删除数据(完成数据的 CRUD 操作)。CRUD 操作对应的 HTTP 请求如下:
- POST: 向数据集中添加新数据。
- GET:检索现有数据
- PUT: 更新现有数据
- DELETE: 删除现有数据
2. 第一步:创建数据集和一个简单的 API
第一步是规划基本元素并将其添加到程序中。要构建 API,需要使用以下包。
- encoding/json: 这个包可以把从请求中接收到的 JSON 数据解析为 Go 数据,反之亦然。
- log: log 包允许为 API 实现日志功能,例如记录请求中的错误。
- net/http:这个包允许接收、解析和发送 HTTP 请求。
创建一个新程序并添加所需的包,如下代码所示:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
2.1 定义数据集
下一步是定义将对其执行 CRUD 操作的数据集。对于这个简单的例子,我们希望从以下字段开始。
- Number: 帐号编号
- Balance: 当前帐户余额
- Desc: 帐户类型
这个数据集的定义将作为一个基本表示,但如果有必要,可以包括其他字段。可以将这些字段映射到 JSON 数据集中的相应字段。
type Account struct {
Number string 'json:"AccountNumber"'
Balance string 'json:"Balance"'
Desc string 'json:"AccountDescription"'
}
我们还将创建一个帐户数据集并将其存储在一个切片中。因为有多个不同的函数来访问此数据集,所以在 main 函数之外将它定义为全局变量。直接在结构体之后添加这个变量。
var Accounts []Account
另一种选择是将 Accounts 数据集创建为局部变量,然后将其传递给每个函数。
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"io/ioutil"
)
// we create a type Account that will be used to represent a bank account
type Account struct {
Number string `json:"AccountNumber"`
Balance string `json:"Balance"`
Desc string `json:"AccountDescription"`
}
// we use Accounts as a global variable because it is used by
// several functions in the code
var Accounts []Account
// implement the homePage
// use the ResponseWriter w to display some text when we visit the home page
func homePage(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w, "Welcome to our bank!")
// we can use a print command to log the request or we can log it to a file, etc.
fmt.Println("Endpoint: /")
}
// handleRequests will process HTTP requests and redirect them to
// the appropriate Handle function
func handleRequests() {
// create a router to handle our requests from the mux package.
router := mux.NewRouter().StrictSlash(true)
// access root page
router.HandleFunc("/", homePage)
// returnAllAccounts
router.HandleFunc("/accounts", returnAllAccounts)
// return requested account
router.HandleFunc("/account/{number}", returnAccount)
// create new account
router.HandleFunc("/account", createAccount).Methods("POST")
// delete requested account
router.HandleFunc("/account/{number}", deleteAccount).Methods("DELETE")
// define the localhost
log.Fatal(http.ListenAndServe(":10000", router))
}
// return the dataset Accounts in a JSON format
func returnAllAccounts(w http.ResponseWriter, r *http.Request){
// we use the Encode function to convert the Account slice into a json object
json.NewEncoder(w).Encode(Accounts)
}
// return a single account
func returnAccount(w http.ResponseWriter, r *http.Request){
vars := mux.Vars(r)
key := vars["number"]
for _, account := range Accounts {
if account.Number == key {
json.NewEncoder(w).Encode(account)
}
}
}
// create a new account
func createAccount(w http.ResponseWriter, r *http.Request) {
reqBody, _ := ioutil.ReadAll(r.Body)
var account Account
json.Unmarshal(reqBody, &account)
Accounts = append(Accounts, account)
json.NewEncoder(w).Encode(account)
}
func deleteAccount(w http.ResponseWriter, r *http.Request) {
// use mux to parse the path parameters
vars := mux.Vars(r)
// extract the account number of the account we wish to delete
id := vars["number"]
// we then need to loop through dataset
for index, account := range Accounts {
// if our id path parameter matches one of our
// account numbers
if account.Number == id {
// updates our dataset to remove the account
Accounts = append(Accounts[:index], Accounts[index + 1:]...)
}
}
}
func main() {
// initialize the dataset
Accounts = []Account{
Account{Number: "C45t34534", Balance: "24545.5", Desc: "Checking Account"},
Account{Number: "S3r53455345", Balance: "444.4", Desc: "Savings Account"},
}
// execute handleRequests, which will kick off the API
// we can access the API using the the URL defined above
handleRequests()
}