百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

使用 Golang 创建 RESTful API | 教程Edge.net

toyiye 2024-06-24 19:09 10 浏览 0 评论

如果您正在编写任何形式的 Web 应用程序,那么您很可能会与 1 个或多个 REST API 交互,以填充应用程序的动态部分并执行诸如更新或删除数据库中的数据之类的任务。

在本教程中,你将要建立一个完全成熟的REST API 暴露GET,POST,DELETE和PUT端点随后将允许你进行全方位的CRUD操作。

为了保持简单并专注于基本概念,我们不会与任何后端数据库技术交互来存储我们将使用的文章。但是,我们将以这样一种方式编写此书 REST API,以便可以轻松更新我们将定义的函数,以便它们对数据库进行后续调用以执行任何必要的CRUD操作。

如果您想了解有关如何使用 Go 与数据库交互的更多信息,可以查看以下文章:

  • 转到 MySQL 教程
  • 去 ORM 教程

源代码- 本文的完整源代码可以在这里找到: TutorialEdge/create-rest-api-in-go-tutorial

先决条件

  • 您需要在开发机器上安装 Go 1.11+ 版本。

目标

在本教程结束时,您将知道如何在 Go 中创建自己的 REST-ful API,以处理所有方面的问题。你会知道如何创建您的项目中REST端点,可以处理POST,GET,PUT和 DELETEHTTP请求。

视频教程


REST 架构

如今,REST 无处不在,从网站到企业应用程序,RESTful 架构风格是一种在独立软件组件之间提供通信的强大方式。构建 REST API 可让您轻松分离消费者和生产者,并且通常设计为无状态。

注 -如果您想了解有关 REST API 基础知识的更多信息,请查看什么是 RESTful API?

JSON

出于本教程的目的,我将使用 JavaScript 对象表示法作为发送和接收所有信息的方式,幸运的是 Go 为使用标准库包 encoding/json 对这些格式的编码和解码提供了一些出色的支持。

注意 -有关 encoding/json 您的更多信息,请查看官方文档: encoding/json

编组

为了让我们更容易我们可以通过使用称为编组的东西轻松地将 GO 将数据结构转换为 JSON,它会生成一个字节切片,其中包含一个非常长的字符串,没有多余的空白。

基本 API 入门

首先,我们必须创建一个可以处理 HTTP 请求的是非常简单的服务器。为此,我们将创建一个名为main.go. 在这个 main.go文件中,我们需要定义 3 个不同的函数。一个homePage函数将处理对我们的根 URL 的所有请求,一个handleRequests将 URL 路径匹配与定义的函数匹配的函数以及一个main将启动我们的 API 的函数。

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func homePage(w http.ResponseWriter, r *http.Request){
    fmt.Fprintf(w, "Welcome to the HomePage!")
    fmt.Println("Endpoint Hit: homePage")
}

func handleRequests() {
    http.HandleFunc("/", homePage)
    log.Fatal(http.ListenAndServe(":10000", nil))
}

func main() {
    handleRequests()
}

如果我们现在在我们的机器上运行它,我们应该看到我们非常简单地 API 在端口 10000 上次启动,如果它没有被另一个进程占用。如果我们现在http://localhost:10000/在本地浏览器中导航到,我们应该会Welcome to the HomePage!在屏幕上看到 打印出来的内容。这意味着我们已经成功创建了我们将构建 REST API 的基础。

注意 -如果您想要更深入地关于如何创建基于 Go 的 Web 服务器的教程,请在此处查看本教程: Creating a Simple Web Server with Go(Lang)

我们的文章结构

我们将创建一个REST API,使我们能够CREATE,READ,UPDATE和 DELETE我们网站上的文章。当我们谈论CRUDAPI 时,我们指的是可以处理所有这些任务的 API:创建、读取、更新和删除。

在开始之前,我们必须定义我们的Article结构。Go 具有这种结构的概念,非常适合这种情况。让我们创建一个Article具有标题、描述 (desc) 和内容的 结构,如下所示:

type Article struct {
    Title string `json:"Title"`
    Desc string `json:"desc"`
    Content string `json:"content"`
}

// let's declare a global Articles array
// that we can then populate in our main function
// to simulate a database
var Articles []Article

我们的 Struct 包含代表我们网站上所有文章所需的 3 个属性。为了使其工作,我们还必须将 "encoding/json"包导入到我们的导入列表中。

现在让我们更新我们的main函数,以便我们的Articles变量填充一些我们可以稍后检索和修改的虚拟数据。

func main() {
    Articles = []Article{
        Article{Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

完美,让我们现在继续创建我们的/articles端点,它将返回我们刚刚在这里定义的所有文章。

检索所有文章

在本教程的这一部分中,我们将创建一个新的 REST 端点,当收到HTTP GET请求时,它将返回我们站点的所有文章。

我们将首先创建一个名为 的新函数returnAllArticles,该函数将执行返回新填充的Articles以 JSON 格式编码的变量的简单任务:

main.go

func returnAllArticles(w http.ResponseWriter, r *http.Request){
    fmt.Println("Endpoint Hit: returnAllArticles")
    json.NewEncoder(w).Encode(Articles)
}

调用将json.NewEncoder(w).Encode(article)我们的文章数组编码为 JSON 字符串,然后作为响应的一部分写入。

在这生效之前,我们还需要向我们的handleRequests 函数添加一个新路由,它将所有调用映射http://localhost:10000/articles到我们新定义的函数。

func handleRequests() {
    http.HandleFunc("/", homePage)
    // add our articles route and map it to our 
    // returnAllArticles function like so
    http.HandleFunc("/articles", returnAllArticles)
    log.Fatal(http.ListenAndServe(":10000", nil))
}

现在我们已经完成了这项工作,通过键入运行代码go run main.go,然后http://localhost:10000/articles在浏览器中打开,您应该会看到文章列表的 JSON 表示,如下所示:

http://localhost:10000/articles 响应

[
  {
    Title: "Hello",
    desc: "Article Description",
    content: "Article Content"
  },
  {
    Title: "Hello 2",
    desc: "Article Description",
    content: "Article Content"
  }
];

我们已经成功定义了我们的第一个 API 端点。

在本系列的下一部分中,您将更新 REST API 以使用gorilla/mux路由器而不是传统net/http路由器。

交换路由器将使您能够更轻松地执行任务,例如解析可能驻留在HTTP我们稍后需要的传入请求中的任何路径或查询参数 。

路由器入门

现在标准库足以提供让您自己的简单 REST API 启动和运行所需的一切,但是现在我们已经掌握了基本概念,我觉得是时候引入第三方路由器包了。最著名和使用率最高的是 gorilla/mux 路由器,它目前在 Github 上有 2,281 颗星。

构建我们的路由器

我们可以更新我们现有的main.go文件并交换一个gorilla/mux 基于HTTP路由器的路由器来代替之前存在的标准库。

修改您的handleRequests函数以创建一个新的路由器。

main.go

package main

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

… // Existing code from above
func handleRequests() {
    // creates a new instance of a mux router
    myRouter := mux.NewRouter().StrictSlash(true)
    // replace http.HandleFunc with myRouter.HandleFunc
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/all", returnAllArticles)
    // finally, instead of passing in nil, we want
    // to pass in our newly created router as the second
    // argument
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

func main() {
    fmt.Println("Rest API v2.0 - Mux Routers")
    Articles = []Article{
        Article{Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

当您现在运行它时,您不会看到我们系统的工作方式发生真正的变化。它仍然会在同一个端口上启动并返回相同的结果,具体取决于您点击的端点。

唯一真正的区别是我们现在有一个 gorilla/mux 路由器,它允许我们在本教程后面轻松地执行诸如检索路径和查询参数之类的操作。

$ 去运行 main.go

Rest API v2.0 - Mux Routers

路径变量

到目前为止一切顺利,我们已经创建了一个非常简单的 REST API,它返回一个主页和我们所有的文章。但是如果我们只想查看一篇文章会发生什么?

好吧,多亏了 gorilla mux 路由器,我们可以将变量添加到我们的路径中,然后根据这些变量选择我们想要返回的文章。在您的handleRequests()功能中创建一条新路线,就在我们的/articles路线下方:

myRouter.HandleFunc("/article/{id}", returnSingleArticle)

请注意,我们已经添加{id}到我们的路径中。这将代表我们的 id 变量,当我们希望仅返回具有该确切键的文章时,我们将能够使用该变量。目前,我们的Article结构没有 Id 属性。现在让我们补充一下:

type Article struct {
    Id      string `json:"Id"`
    Title   string `json:"Title"`
    Desc    string `json:"desc"`
    Content string `json:"content"`
}

然后我们可以更新我们的main函数以填充数组中的Id值 Articles:

func main() {
    Articles = []Article{
        Article{Id: "1", Title: "Hello", Desc: "Article Description", Content: "Article Content"},
        Article{Id: "2", Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
    }
    handleRequests()
}

现在我们已经完成了,在我们的returnSingleArticle函数中,我们可以{id}从我们的 URL 中获取这个值,我们可以返回符合这个条件的文章。由于我们没有将数据存储在任何地方,因此我们只会返回传递给浏览器的 Id。

func returnSingleArticle(w http.ResponseWriter, r *http.Request){
    vars := mux.Vars(r)
    key := vars["id"]

    fmt.Fprintf(w, "Key: " + key)
}

如果我们http://localhost:1000/article/1在现在运行之后导航到,您应该会看到Key: 1在浏览器中打印出来。

让我们使用此key值返回与该键匹配的特定文章。

func returnSingleArticle(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    key := vars["id"]

    // Loop over all of our Articles
    // if the article.Id equals the key we pass in
    // return the article encoded as JSON
    for _, article := range Articles {
        if article.Id == key {
            json.NewEncoder(w).Encode(article)
        }
    }
}

通过调用运行它go run main.go,然后http://localhost:10000/article/1 在浏览器中打开:

http://localhost:10000/article/1 响应

{
Id: "1",
Title: "Hello",
desc: "Article Description",
content: "Article Content"
}

您现在将看到与1作为 JSON 返回的键匹配的文章。

创建和更新文章

在教程的这一部分,你将要打造的Create,Update和 DELETE一个部分CRUDREST API。我们已经涵盖了R阅读单个文章和所有文章的能力。

创建新文章

再一次,您将需要创建一个新函数来完成创建这篇新文章的工作。

让我们从createNewArticle()在我们的main.go文件中创建一个函数开始。

func createNewArticle(w http.ResponseWriter, r *http.Request) {
    // get the body of our POST request
    // return the string response containing the request body    
    reqBody, _ := ioutil.ReadAll(r.Body)
    fmt.Fprintf(w, "%+v", string(reqBody))
}

定义此函数后,您现在可以将路由添加到函数内定义的路由列表中handleRequests。但是,这一次,我们将添加.Methods("POST") 到路由的末尾以指定我们只想在传入请求是HTTP POST请求时调用此函数:

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/articles", returnAllArticles)
    // NOTE: Ordering is important here! This has to be defined before
    // the other `/article` endpoint. 
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    myRouter.HandleFunc("/article/{id}", returnSingleArticle)
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

再次尝试运行它,然后尝试提交HTTP POST包含以下POST正文的请求:

{
    "Id": "3", 
    "Title": "Newly Created Post", 
    "desc": "The description for my new post", 
    "content": "my articles content" 
}

我们的端点将触发并随后回显请求正文中的任何值。

现在您已经验证了新端点是否正常工作,让我们更新我们的createNewArticle函数,以便它将请求正文中的 JSON 解组为一个新的Article结构体,该结构体随后可以附加到我们的Articles数组中:

func createNewArticle(w http.ResponseWriter, r *http.Request) {
    // get the body of our POST request
    // unmarshal this into a new Article struct
    // append this to our Articles array.    
    reqBody, _ := ioutil.ReadAll(r.Body)
    var article Article 
    json.Unmarshal(reqBody, &article)
    // update our global Articles array to include
    // our new Article
    Articles = append(Articles, article)

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

惊人的!如果您现在运行它POST并向您的应用程序发送相同的请求,您将看到它返回与以前相同的 JSON 格式,但它还会将新文章附加到您的Articles数组中。

现在通过点击来验证http://localhost:10000/articles:

http://localhost:10000/articles 响应

[
    {
        "Id": "1",
        "Title": "Hello",
        "desc": "Article Description",
        "content": "Article Content"
    },
    {
        "Id": "2",
        "Title": "Hello 2",
        "desc": "Article Description",
        "content": "Article Content"
    },
    {
        "Id": "3",
        "Title": "Newly Created Post",
        "desc": "The description for my new post",
        "content": "my articles content"
    }
]

您现在已经成功地向Create新的 REST API添加了一个函数!

在本教程的下一部分中,您将了解如何添加新的 API 端点以允许您删除文章。

删除文章

有时您可能需要删除 REST API 公开的数据。为此,您需要DELETE在 API 中公开一个端点,该端点将接收一个标识符并删除与该标识符关联的任何内容。

在本教程的这一部分中,您将创建另一个端点,该端点接收HTTP DELETE请求并删除与给定Id路径参数匹配的文章 。

向您的main.go文件添加一个新函数,我们将调用该函数deleteArticle:

func deleteArticle(w http.ResponseWriter, r *http.Request) {
    // once again, we will need to parse the path parameters
    vars := mux.Vars(r)
    // we will need to extract the `id` of the article we
    // wish to delete
    id := vars["id"]

    // we then need to loop through all our articles
    for index, article := range Articles {
        // if our id path parameter matches one of our
        // articles
        if article.Id == id {
            // updates our Articles array to remove the 
            // article
            Articles = append(Articles[:index], Articles[index+1:]...)
        }
    }

}

再一次,您需要向handleRequests映射到这个新deleteArticle函数的函数添加一个路由:

func handleRequests() {
    myRouter := mux.NewRouter().StrictSlash(true)
    myRouter.HandleFunc("/", homePage)
    myRouter.HandleFunc("/articles", returnAllArticles)
    myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
    // add our new DELETE endpoint here
    myRouter.HandleFunc("/article/{id}", deleteArticle).Methods("DELETE")
    myRouter.HandleFunc("/article/{id}", returnSingleArticle)
    log.Fatal(http.ListenAndServe(":10000", myRouter))
}

尝试向 发送新HTTP DELETE请求http://localhost:10000/article/2。这将删除您的文章数组中的第二篇文章,当您随后http://localhost:10000/articles发出HTTP GET请求时,您应该看到它现在只包含一个Article.

注意- 为简单起见,我们正在更新一个全局变量。但是,我们不会进行任何检查以确保我们的代码没有竞争条件。为了使这段代码线程安全,我建议查看我关于Go Mutexes 的其他教程

更新文章端点

您需要实现的最后一个端点是 Update 端点。这个端点将是一个HTTP PUT基于端点,需要接受一个Id路径参数,就像我们对我们的HTTP DELETE端点所做的一样,以及一个 JSON 请求正文。

传入HTTP PUT请求正文中的此 JSON将包含我们要更新的文章的较新版本。

挑战

尝试在updateArticle函数中创建一个函数和相应的路由 handleRequests。这将与PUT请求匹配。完成此操作后,请使用您在函数中使用的相同代码实现该updateArticle函数,以便它解析HTTP请求正文createNewArticle。

最后,您必须遍历Articles数组中的文章并匹配并随后更新文章。

结论

这个例子代表了一个使用 Go 编写的非常简单的 RESTful API。在实际项目中,我们通常会将其与数据库联系起来,以便我们返回真实值。

源代码- 本教程的完整源代码可以在这里找到: TutorialEdge/create-rest-api-in-go

进一步阅读

如果您喜欢这篇文章,那么您可能还会喜欢以下教程:

  • 设计生产就绪的 REST API
  • 转到 MySQL 教程

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码