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

用Golang实现一个带缓存的REST API服务端

toyiye 2024-06-21 11:59 9 浏览 0 评论

REST API原理

REST(Representational State Transfer)是一种通过HTTP协议设计API的架构风格,用于构建分布式系统中的网络应用程序。REST API(RESTful API)是基于这种设计风格的应用程序编程接口。其主要优点是其极大的灵活性, 只要需要直接从服务器向Web应用程序或站点的用户提供数据, 开发人员直接使用REST API即可实现。

REST API原理图

REST API 的设计目的是创建简单、可伸缩、可维护且具有良好可读性的接口, 以促进客户端和服务器之间的有效通信, 通过使用HTTP协议和一组统一的设计原则, REST API在实现上具备一些特定的属性。

  • 资源(Resources):在REST中, 数据或服务都被视为资源。每个资源都有一个唯一的标识符, 用于在网络上标识和定位该资源。
  • 表现层(Representation):资源的状态可以以不同的表现形式呈现, 例如: JSON、XML或HTML。客户端可以通过请求特定的表现形式来与服务器交互。
  • 状态无关(Stateless):REST是状态无关的, 这意味着每个请求从客户端到服务器都包含了足够的信息, 服务器不需要存储客户端的状态。每个请求都应该包含了执行该请求所需的所有信息。
  • 统一接口(Uniform Interface):RESTful API的设计应该遵循一致的接口原则, 使得不同的组件之间的通信变得简单统一。
  • 无状态通信(Stateless Communication):每个请求从客户端到服务器都应该包含足够的信息, 以便服务器能够理解和处理请求,而无需依赖之前的请求。
  • 缓存(Cacheability):REST API支持缓存, 以提高性能和减轻服务器的负担。服务器可以在响应中指定数据的缓存策略,而客户端可以使用缓存来避免重复请求相同的数据。
  • 使用标准方法(Standard Methods):REST使用标准的HTTP方法,如:GET、POST、PUT、DELETE来执行不同的操作, 这些方法对应于对资源的不同操作, 使得API的使用更加直观和符合HTTP标准。

REST API服务端设计

下面我将使用Go语言来设计一个REST API的服务端, 这里的模拟场景是通过服务端来对外提供文章的增、删、查服务, 文章的查询方式包括两种: 1.查询服务器所有文章内容。2.根据文章ID查询某篇文章的内容。

文章的属性包括三个字段: 文章ID、文章标题、文章内容, 这里可以用一个结构体表示:

type Article struct {
	ID      string `json:"id,omitempty"`
	Title   string `json:"title,omitempty"`
	Content string `json:"content,omitempty"`
}

由于我们设计了四个功能接口, 将其转换成接口代码框架如下:

// 获取所有文章接口
func GetArticles(w http.ResponseWriter, r *http.Request) {
     ...
}

// 获取单篇文章接口
func GetArticle(w http.ResponseWriter, r *http.Request) {
     ...
}

// 创建文章接口
func CreateArticle(w http.ResponseWriter, r *http.Request) {
     ...
}

// 删除文章接口
func DeleteArticle(w http.ResponseWriter, r *http.Request) {
     ...
}

功能代码实现

首先来实现获取所有文章的接口, 具体参考代码如下:

// 获取所有文章
func GetArticles(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	// 尝试从缓存中获取文章列表
	if cachedArticles, found := articleCache.Load("all"); found {
		json.NewEncoder(w).Encode(cachedArticles)
		return
	}

	// 从原始数据源获取文章列表
	json.NewEncoder(w).Encode(Articles)

	// 将文章列表存入缓存
	articleCache.Store("all", Articles)
}

获取所有文章首先是尝试从缓存中获取文章列表, 缓存cachedArticles这里实际是一个sync.Map类型的变量, 支持并发安全, 如果找到的话, 直接序列化成JSON的格式返回。如果缓存中没有找到, 则从原始数据源中获取文章列表(实际应用中应该是从数据库中获取)并序列化成JSON返回。并把该文章列表存入缓存之中。

接下来实现第二个接口,通过文章ID来查询, 实现参考代码如下:

// 获取单篇文章
func GetArticle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)

	// 尝试从缓存中获取单个文章
	if cachedArticle, found := articleCache.Load(params["id"]); found {
		json.NewEncoder(w).Encode(cachedArticle)
		return
	}

	// 从原始数据源获取单个文章
	for _, article := range Articles {
		if article.ID == params["id"] {
			json.NewEncoder(w).Encode(article)
			// 将单个文章存入缓存
			articleCache.Store(params["id"], article)
			return
		}
	}
	json.NewEncoder(w).Encode(&Article{})
}

首先将传入的ID参数在缓存中进行查找, 找到则直接返回JSON数据。如果没有找到则继续在文章列表中查找, 并将单篇文章存入缓存。

创建文章接口参考代码如下:

// 创建文章
func CreateArticle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var article Article
	_ = json.NewDecoder(r.Body).Decode(&article)
	Articles = append(Articles, article)

	// 清除所有文章缓存
	articleCache.Delete("all")

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

创建文章列表需要注意的是, 这里为了维护缓存一致性,避免脏数据, 对缓存进行了清空, 以便下次GetArticles()时更新最新的缓存。

同理也不难实现删除文章的接口:

// 删除文章
func DeleteArticle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)

	// 清除单个文章缓存
	articleCache.Delete(params["id"])

	for index, article := range Articles {
		if article.ID == params["id"] {
			Articles = append(Articles[:index], Articles[index+1:]...)
			break
		}
	}

	// 清除所有文章缓存
	articleCache.Delete("all")

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

最后, 在main函数中, 我们需要往列表中添加一些数据来模拟服务端保存的文章数, 并且定义四个接口的路由, 整体代码如下:

package main

import (
	"encoding/json"
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/mux"
)

// Article 结构体表示 API 中的数据模型
type Article struct {
	ID      string `json:"id,omitempty"`
	Title   string `json:"title,omitempty"`
	Content string `json:"content,omitempty"`
}

// Articles 数组用于存储文章数据
var Articles []Article
var articleCache sync.Map

// 获取所有文章
func GetArticles(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	// 尝试从缓存中获取文章列表
	if cachedArticles, found := articleCache.Load("all"); found {
		json.NewEncoder(w).Encode(cachedArticles)
		return
	}

	// 从原始数据源获取文章列表
	json.NewEncoder(w).Encode(Articles)

	// 将文章列表存入缓存
	articleCache.Store("all", Articles)
}

// 获取单个文章
func GetArticle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)

	// 尝试从缓存中获取单个文章
	if cachedArticle, found := articleCache.Load(params["id"]); found {
		json.NewEncoder(w).Encode(cachedArticle)
		return
	}

	// 从原始数据源获取单个文章
	for _, article := range Articles {
		if article.ID == params["id"] {
			json.NewEncoder(w).Encode(article)
			// 将单个文章存入缓存
			articleCache.Store(params["id"], article)
			return
		}
	}
	json.NewEncoder(w).Encode(&Article{})
}

// 创建文章
func CreateArticle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var article Article
	_ = json.NewDecoder(r.Body).Decode(&article)
	Articles = append(Articles, article)

	// 清除所有文章缓存
	articleCache.Delete("all")

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

// 删除文章
func DeleteArticle(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)

	// 清除单个文章缓存
	articleCache.Delete(params["id"])

	for index, article := range Articles {
		if article.ID == params["id"] {
			Articles = append(Articles[:index], Articles[index+1:]...)
			break
		}
	}

	// 清除所有文章缓存
	articleCache.Delete("all")

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


func main() {
	// 初始化数据
	Articles = append(Articles, Article{ID: "1", Title: "Article 1", Content: "Content 1"})
	Articles = append(Articles, Article{ID: "2", Title: "Article 2", Content: "Content 2"})

	// 创建路由器
	router := mux.NewRouter()

	// 定义路由处理程序
	router.HandleFunc("/articles", GetArticles).Methods(http.MethodGet)
	router.HandleFunc("/articles/{id}", GetArticle).Methods(http.MethodGet)
	router.HandleFunc("/articles", CreateArticle).Methods(http.MethodPost)
	router.HandleFunc("/articles/{id}", DeleteArticle).Methods(http.MethodDelete)

	// 启动服务器
	log.Fatal(http.ListenAndServe(":8080", router))
}

实际运行效果

在本地运行服务端, 服务端将在本地监听8080端口, 通过浏览器输入: http://127.0.0.1:8080/articles, 该接口将获取所有文章,如图:

获取所有文章返回的结果

通过API接口通过文章ID查询某一篇文章,接口请求如下:

通过ID查询获取指定文章返回结果

新增一条新文章是POST请求, 这里主要通过Apifox发送POST请求,如图:

新增一篇文章返回的结果

从返回的结果来看, 成功添加了一条新记录, 再次使用获取全部文章接口看一下,如图:

确认文章添加成功

新记录确实添加成功。

最后, 还是通过Apifox,我们发送一个删除ID为2的文章请求,如图:

删除文章接口返回的结果

从Apifox返回的结果来看,确实删除成功了, 现在再获取一下全部文章,如图:

验证删除文章成功返回结果

至此,所有接口全部验证成功。

相关推荐

为何越来越多的编程语言使用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)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码