本文章分为上下两章,在上篇文章里我们将介绍API的概念、快速构建服务、使用 gin 构建 API 接口、JWT。在下篇文章里我们将使用本文章所讲述的技术实现用户认证功能API。
1 API概念
API(Application Programming Interface,应用程序编程接口)是一种允许不同软件应用程序之间进行交互的方式。它定义了一组规则和协议,通过这些规则和协议,不同的软件组件、服务或应用可以相互通信和交换数据。API 允许开发人员在不暴露内部实现细节的情况下,与外部组件进行集成。
API 可以用于不同类型的应用,包括:
- Web API:
Web API 是通过网络访问的一种 API,通常使用 HTTP 协议。Web API 可以提供数据、服务、功能等,供其他应用或客户端使用。常见的例子包括 RESTful API、GraphQL 等。 - 库 API:
库 API 是针对特定编程语言的一组函数、方法和类,开发人员可以通过调用这些函数来访问库中提供的功能。例如,标准库或第三方库提供的函数集合。 - 操作系统 API:
操作系统 API 允许应用程序与操作系统进行交互,访问底层系统资源和功能,如文件系统、网络、内存管理等。 - 硬件 API:
硬件 API 允许应用程序与硬件设备进行交互,如图形处理器、传感器、打印机等。
API 的重要性在于它们允许不同的软件系统协同工作,实现功能复用、解耦合、模块化开发等优势。通过定义清晰的接口,API 可以促进不同开发团队、不同项目之间的合作,同时也可以为开发人员提供更高效的开发体验。常见的 API 通信方式包括使用 HTTP 请求和响应,如 GET、POST、PUT、DELETE 等。
2 快速构建
以下短短几行代码即可快速构建并启动API服务。要访问API只需要在浏览器中输入http://localhost:8080/test
即可看到“Hello!”。
http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(w, "Hello!")})
log.Fatal(http.ListenAndServe(":8080", nil))
3 使用 gin 构建 API 接口
3.1 gin的介绍
Gin 是一个基于 Go 语言的轻量级 Web 框架,专注于高性能和开发效率。它是一个快速的 HTTP 框架,旨在帮助开发人员构建高性能的 Web 应用程序和 API 服务。
以下是 Gin 框架的一些特点和功能:
- 快速:Gin 通过最小化框架内部的功能和开销,追求极致的性能。它采用了一些优化措施,如使用 Radix 树进行路由匹配,从而使得处理请求的速度更快。
- 轻量级:Gin 的代码库相对较小,它只包含一些基本的功能,这使得它很容易上手并且不会引入过多的复杂性。
- 路由:Gin 提供了灵活的路由设置和参数解析,支持 RESTful 风格的路由设计,使得创建 API 接口变得简单和直观。
- 中间件:Gin 支持中间件,开发人员可以在请求处理过程中插入额外的逻辑,例如日志记录、认证、跨域处理等。
- 错误处理:Gin 提供了一种统一的错误处理机制,开发人员可以返回具有适当状态码和错误消息的错误响应。
- JSON 解析和序列化:Gin 提供了方便的 JSON 解析和序列化功能,可以轻松地处理请求和响应中的 JSON 数据。
- HTML 模板渲染:虽然 Gin 主要用于构建 API 接口,但它也支持基本的 HTML 模板渲染功能,使其适用于一些 Web 应用场景。
- 扩展性:虽然 Gin 是一个轻量级框架,但它支持自定义中间件、插件和拓展,开发人员能够根据需求扩展功能。
3.2 使用gin快速构建
同样的,我们要访问API只需要在浏览器中输入http://localhost:8080/test
即可看到“Hello!”。
engine := gin.Default()
engine.GET("/test", func(c *gin.Context) {
c.String(http.StatusOK, "Hello!")
})
engine.Run(":8080")
以上主要分为以下三步:
- 初始化http服务对象(gin.Default)
- 设置路由(engine.GET)
- 监听服务(engine.Run)
3.3 HTTP的方法
在 RESTful API 中,使用的主要是以下五种HTTP方法:
- GET,表示读取服务器上的资源
- POST,表示在服务器上创建资源
- PUT,表示更新或者替换服务器上的资源
- DELETE,表示删除服务器上的资源
- PATCH,表示更新/修改资源的一部分
此外还有不常用的HEAD、OPTIONS、TRACE、CONNECT等方法
要实现以上方法,只需要engine.method(*)
就可以了。如果要实现所有method,只需要engine.Any(*)
就可以了。在这里给大家看一下Any的实现函数:
var anyMethods = []string{
http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch,
http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect,
http.MethodTrace,
}
// Any registers a route that matches all the HTTP methods.
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
for _, method := range anyMethods {
group.handle(method, relativePath, handlers)
}
return group.returnObj()
}
3.4 拆分
在真正写服务中我们不可能会把一大堆代码写到一个.go文件中,而是会拆分为controller包、repository包、util和public等包,其中public放置静态资源文化,而controller包中就是进行处理的Handler,再有就是repository包用来连接数据库。对于路由的注册习惯是写一个router.go文件来统一管理路由。
4 JWT
4.1 JWT的概念
JWT(JSON Web Token)是一种用于在网络应用间传递信息的开放标准(RFC 7519)。它以 JSON 格式存储信息,通过数字签名或加密来确保信息的安全传输。JWT 通常用于身份验证和授权,允许两个系统之间安全地传递信息,以便进行用户身份验证和授权控制。
JWT 由三部分组成,每个部分之间使用点号(.)分隔:
- Header(头部) :包含两部分信息:令牌类型(通常是 "JWT")和所使用的签名算法,如 HMAC SHA256 或 RSA。
- Payload(载荷) :也称为声明,包含需要传递的信息。声明分为注册声明(Registered Claims)、公共声明(Public Claims)和私有声明(Private Claims)。注册声明包括常见的声明,如发行人、过期时间等。公共声明包含不敏感的信息,可以自定义添加。私有声明包含应用程序特定的信息。
- Signature(签名) :使用头部和载荷以及一组密钥进行签名,以确保消息的完整性和认证。签名部分用于验证发送者是否为其声称的发送者,以及载荷在传输过程中是否被篡改。
JWT 的工作流程通常如下:
- 用户登录时,服务器生成一个 JWT 并将用户信息和其他所需信息添加到载荷部分。
- 服务器使用密钥对 JWT 进行签名,生成签名部分。
- 服务器将生成的 JWT 返回给客户端。
- 客户端在以后的请求中将 JWT 添加到请求的头部或其他位置,以便服务器验证用户身份。
- 服务器接收到 JWT 后,可以验证签名、解析载荷并执行授权和认证操作。
JWT 具有以下优点:
- 轻量且自包含:JWT 可以在令牌中携带用户信息,避免了频繁的数据库查询,因此适用于分布式系统。
- 无状态:JWT 本身包含了所有必要的信息,服务器不需要在后端存储会话信息。
- 跨域和多平台支持:JWT 可以在不同的系统和域之间使用,方便传递认证信息。
需要注意:
- JWT 中的信息是可解码的,因此不应在 JWT 中存储敏感信息。
- 由于 JWT 是无状态的,一旦签发,就不能撤销。如果需要撤销访问权限,需要等待 JWT 过期。
4.2 JWT的使用
使用JWT主要有以下几步:
- 获取request
- 根据传入的request生成token
- 返回response
具体JWT的使用见下篇文章。