Go从入门到精通(15)-包(package)

Go从入门到精通(9)-函数


文章目录

  • Go从入门到精通(15)-包(package)
  • Go从入门到精通(9)-函数
  • 前言
  • go+gin搭建web api 服务
    • API 接口说明
      • 用户认证
      • 用户管理
      • 通用接口
    • 测试API
      • 注册新用户
      • 用户登录
      • 获取用户信息(需要认证)
    • gin 代码说明
      • Gin 核心概念
        • 路由 (Router)
          • 路由分组
        • 中间件 (Middleware)
        • 上下文 (Context)
  • 后续扩展计划


前言

本章开始,我们以一个真实项目的形式来展现前面所学的内容。同时会引入一些常用的第三方包和业内常用的场景。这个示例实现了用户管理、认证和基本的 CRUD 操作。

go+gin搭建web api 服务

先看代码

package mainimport ("fmt""net/http""time""github.com/dgrijalva/jwt-go""github.com/gin-contrib/cors""github.com/gin-gonic/gin""golang.org/x/crypto/bcrypt"
)// 配置信息
const (secretKey       = "your-secret-key"tokenExpiration = 24 * time.Hour
)// User 用户模型
type User struct {ID       string `json:"id"`Username string `json:"username"`Password string `json:"password,omitempty"`Email    string `json:"email"`
}// LoginRequest 登录请求
type LoginRequest struct {Username string `json:"username" binding:"required"`Password string `json:"password" binding:"required"`
}// RegisterRequest 注册请求
type RegisterRequest struct {Username string `json:"username" binding:"required"`Password string `json:"password" binding:"required,min=6"`Email    string `json:"email" binding:"required,email"`
}// TokenResponse 令牌响应
type TokenResponse struct {Token string `json:"token"`
}// 模拟数据库
var users = make(map[string]User)
var nextUserID = 1func main() {// 设置为生产模式// gin.SetMode(gin.ReleaseMode)// 创建默认引擎,包含日志和恢复中间件r := gin.Default()// 配置CORSr.Use(cors.Default())// 公共路由public := r.Group("/api/public"){public.POST("/register", RegisterHandler)public.POST("/login", LoginHandler)public.GET("/health", healthHandler)}// 认证路由auth := r.Group("/api/v1/auth")auth.Use(AuthMiddleware()){auth.GET("/users/me", GetCurrentUserHandler)auth.GET("/users", GetUsersHandler)auth.GET("/users/:id", GetUserHandler)auth.PUT("/users/:id", UpdateUserHandler)auth.DELETE("/users/:id", DeleteUserHandler)}// 启动服务器fmt.Println("Server started at :8080")if err := r.Run(":8080"); err != nil {fmt.Println("Failed to start server:", err)}
}// 注册处理
func RegisterHandler(c *gin.Context) {var request RegisterRequest// 绑定并验证请求if err := c.ShouldBindJSON(&request); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// 检查用户名是否已存在for _, user := range users {if user.Username == request.Username {c.JSON(http.StatusConflict, gin.H{"error": "Username already exists"})return}}// 哈希密码hashedPassword, err := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})return}// 创建新用户userID := fmt.Sprintf("%d", nextUserID)nextUserID++user := User{ID:       userID,Username: request.Username,Password: string(hashedPassword),Email:    request.Email,}// 保存用户users[userID] = user// 生成令牌token, err := generateToken(userID)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})return}c.JSON(http.StatusCreated, TokenResponse{Token: token})
}// 登录处理
func LoginHandler(c *gin.Context) {var request LoginRequest// 绑定并验证请求if err := c.ShouldBindJSON(&request); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// 查找用户var user Userfor _, u := range users {if u.Username == request.Username {user = ubreak}}// 验证用户if user.ID == "" {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})return}// 验证密码if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.Password)); err != nil {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})return}// 生成令牌token, err := generateToken(user.ID)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})return}c.JSON(http.StatusOK, TokenResponse{Token: token})
}// 获取当前用户
func GetCurrentUserHandler(c *gin.Context) {userID := c.MustGet("user_id").(string)user, exists := users[userID]if !exists {c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})return}// 不返回密码user.Password = ""c.JSON(http.StatusOK, user)
}// 获取所有用户
func GetUsersHandler(c *gin.Context) {var userList []Userfor _, user := range users {// 不返回密码user.Password = ""userList = append(userList, user)}c.JSON(http.StatusOK, userList)
}// 获取单个用户
func GetUserHandler(c *gin.Context) {userID := c.Param("id")user, exists := users[userID]if !exists {c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})return}// 不返回密码user.Password = ""c.JSON(http.StatusOK, user)
}// 更新用户
func UpdateUserHandler(c *gin.Context) {userID := c.Param("id")currentUserID := c.MustGet("user_id").(string)// 只能更新自己的信息if userID != currentUserID {c.JSON(http.StatusForbidden, gin.H{"error": "Permission denied"})return}user, exists := users[userID]if !exists {c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})return}var updateData struct {Username string `json:"username"`Email    string `json:"email"`Password string `json:"password"`}if err := c.ShouldBindJSON(&updateData); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// 更新字段if updateData.Username != "" {user.Username = updateData.Username}if updateData.Email != "" {user.Email = updateData.Email}if updateData.Password != "" {// 哈希新密码hashedPassword, err := bcrypt.GenerateFromPassword([]byte(updateData.Password), bcrypt.DefaultCost)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})return}user.Password = string(hashedPassword)}// 保存更新users[userID] = user// 不返回密码user.Password = ""c.JSON(http.StatusOK, user)
}// 删除用户
func DeleteUserHandler(c *gin.Context) {userID := c.Param("id")currentUserID := c.MustGet("user_id").(string)// 只能删除自己的账户if userID != currentUserID {c.JSON(http.StatusForbidden, gin.H{"error": "Permission denied"})return}_, exists := users[userID]if !exists {c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})return}// 删除用户delete(users, userID)c.JSON(http.StatusNoContent, nil)
}// Ping处理
func healthHandler(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "success"})
}// 生成JWT令牌
func generateToken(userID string) (string, error) {// 创建令牌token := jwt.New(jwt.SigningMethodHS256)// 设置声明claims := token.Claims.(jwt.MapClaims)claims["id"] = userIDclaims["exp"] = time.Now().Add(tokenExpiration).Unix()// 生成签名字符串return token.SignedString([]byte(secretKey))
}// 认证中间件
func AuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {// 获取授权头authHeader := c.GetHeader("Authorization")if authHeader == "" {c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header missing"})c.Abort()return}// 验证授权头格式if len(authHeader) < 7 || authHeader[:7] != "Bearer " {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid authorization header format"})c.Abort()return}// 提取令牌tokenString := authHeader[7:]// 解析令牌token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {// 验证签名方法if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])}return []byte(secretKey), nil})if err != nil {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})c.Abort()return}// 验证令牌有效性if !token.Valid {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})c.Abort()return}// 提取用户IDclaims, ok := token.Claims.(jwt.MapClaims)if !ok {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})c.Abort()return}userID, ok := claims["id"].(string)if !ok {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user ID in token"})c.Abort()return}// 将用户ID添加到上下文c.Set("user_id", userID)// 继续处理请求c.Next()}
}

API 接口说明

用户认证

POST /api/public/register - 注册新用户
请求体:

{“username”: “user”, “password”: “pass123”, “email”: “user@example.com”}

响应:

{“token”: “JWT_TOKEN”}

POST /api/public/login - 用户登录
请求体:

{“username”: “user”, “password”: “pass123”}

响应:

{“token”: “JWT_TOKEN”}

用户管理

GET /api/v1/auth/users/me - 获取当前用户信息(需认证)
GET /api/v1/auth/users - 获取所有用户列表(需认证)
GET /api/v1/auth/users/:id - 获取指定用户信息(需认证)
PUT /api/v1/authusers/:id - 更新用户信息(需认证,只能更新自己)
DELETE /api/v1/auth/users/:id - 删除用户(需认证,只能删除自己)

通用接口

GET /api/v1/auth/heatth- 测试接口,返回

{
“message”: “success”
}

测试API

注册新用户

curl -X POST -H “Content-Type: application/json” -d ‘{
“username”: “testuser”,
“password”: “testpassword”,
“email”: “test@example.com”
}’ http://localhost:8080/api/public/register

用户登录

curl -X POST -H “Content-Type: application/json” -d ‘{ “username”:
“testuser”, “password”: “testpassword” }’
http://localhost:8080/api/public/login

//结果

{
“token”: “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTIyMTk0NDcsImlkIjoiMSJ9.xNuyMAdFLIfGTVW-fB6slomqTGRmflbnm0t6qO4dKqg”
}

获取用户信息(需要认证)

curl -H “Authorization: Bearer
<eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NTIyMTk0NDcsImlkIjoiMSJ9.xNuyMAdFLIfGTVW-fB6slomqTGRmflbnm0t6qO4dKqg>”
http://localhost:8080/api/v1/auth/users/me

输出

{
“id”: “1”,
“username”: “testuser”,
“email”: “test@example.com”
}

其他接口可以自行验证

真实项目的用户登录场景可能比这个会更加复杂,能会有很多校验,但是大致流程基本如此,我们作为学习已经够用了

gin 代码说明

Gin 是一个用 Go 语言编写的高性能 Web 框架,它提供了简洁的 API 和强大的功能,包括路由、中间件、参数绑定和渲染等。

Gin 核心概念

路由 (Router)

Gin 使用 HTTP 方法(GET、POST、PUT、DELETE 等)和路径模式来定义路由。例如:

r.GET("/heath", healthHandler)  // 处理 GET /ping 请求
r.POST("/login", LoginHandler)  // 处理 POST /login 请求
路由分组
// 公共路由(无需认证)
public := r.Group("/api")
{public.POST("/register", RegisterHandler)public.POST("/login", LoginHandler)
}// 认证路由(需 JWT 令牌)
auth := r.Group("/api")
auth.Use(AuthMiddleware())  // 应用认证中间件
{auth.GET("/users/me", GetCurrentUserHandler)auth.GET("/users", GetUsersHandler)// ...
}
中间件 (Middleware)
// 全局中间件:记录请求日志
r.Use(gin.Logger())// 自定义中间件:认证
auth := r.Group("/api")
auth.Use(AuthMiddleware())  // 对 /api 下的所有路由生效
上下文 (Context)

gin.Context 是 Gin 中最重要的结构体,它封装了 HTTP 请求和响应,提供了参数解析、JSON 序列化、路由跳转等功能。例如:

func PingHandler(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "success"})  // 返回 JSON 响应
}

其他的诸如jwt、加解密等涉及东西比较多,大家可以自行了解。这里主要是了解gin的使用,更多使用方式参考gin官网

后续扩展计划

上面只是简单的搭建一个web Api 服务器,后续我们对其扩展一些常见的使用,包括当不限于下面的,有想法的也可以评论区给我留言

  1. 使用数据库:将内存存储替换为真正的数据库(如 MySQL、PostgreSQL 或 MongoDB)
  2. 添加日志:使用 log包或第三方日志库(如 logrus)
  3. 实现文件上传:添加处理文件上传的 API 添加缓存:
  4. 使用 Redis 缓存频繁访问的数据
  5. 实现邮件服务:添加注册验证邮件和密码重置功能
  6. 添加 Swagger 文档:自动生成 API 文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88767.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88767.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/88767.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python爬虫实战:研究python-docx库相关技术

1. 引言 1.1 研究背景与意义 随着学术资源数字化程度的提高,科研工作者面临海量文献数据的筛选与分析挑战。传统人工调研方式效率低下,难以全面捕捉研究领域的动态趋势。自动化文献分析系统能够通过爬虫技术快速采集多源数据,并通过文本挖掘提取关键信息,为研究方向选择、…

Django中序列化与反序列化

1&#xff1a;序列化&#xff1a;将数据结构或对象状态转换为可以存储或传输的格式&#xff08;如JSON、XML&#xff09;的过程。在Web开发中&#xff0c;通常是将模型实例&#xff08;或查询集&#xff09;转换为JSON格式&#xff0c;以便通过HTTP响应发送给客户端。序列化&am…

【离线数仓项目】——电商域DWD层开发实战

摘要本文主要介绍了离线数仓项目中电商域DWD层的开发实战。DWD层是数据仓库架构中的明细数据层&#xff0c;对ODS层的原始数据进行清洗、规范、整合与业务建模。它具有数据清洗、标准化、业务建模、整合、维度挂载等作用&#xff0c;常见设计特征包括一致性、明细级建模、保留历…

爬虫-正则使用

1.模块选择用re模块导入&#xff0c;&#xff0c;最前面加个r&#xff0c;就不用怕转义了2.模块使用re.findall使用结果是数组方式呈现re.finditer把结果变成迭代器&#xff0c;从迭代器类中间取数re.searchre.search 只能匹配到第一个识别到的内容re.match3.推荐写法先预加载完…

python-range函数

文章目录基本用法重要特性与列表转换注意事项遍历回去列表的元素索引range()是Python中用于生成数字序列的内置函数&#xff0c;常用于循环和序列生成。基本用法 range(stop) # 生成0到stop-1的整数序列 range(start, stop) # 生成start到stop-1的整数序列 r…

汽车功能安全-软件集成和验证(Software Integration Verification)【目的、验证输入、集成验证要求】9

文章目录1 目的2 验证输入3 软件集成要求3.1 要求和建议3.2 汽车行业示例&#xff08;混合动力控制器软件&#xff09;4 验证要求1 目的 软件集成和验证阶段的核心目标是证明集成后的软件单元&#xff08;模块、组件&#xff09;已经正确地开发出来&#xff0c;满足了所有的功…

每天一个前端小知识 Day 27 - WebGL / WebGPU 数据可视化引擎设计与实践

WebGL / WebGPU 数据可视化引擎设计与实践&#x1f3af; 一、为什么前端需要 WebGL / WebGPU&#xff1f; 传统的图表库如 ECharts、Highcharts 基于 Canvas 或 SVG&#xff0c;适合 2D 渲染&#xff0c;但&#xff1a; 当数据量 > 1 万时&#xff0c;SVG 性能瓶颈明显&…

JavaScript代码段注入:动态抓取DOM元素的原理与实践

1.F12打开网页说明&#xff1a;以百度网站为例。通过插入代码块抓取当前网页dom元素。2.新代码段说明&#xff1a;点击源代码/来源菜单项下面的代码段。点击新代码段新增代码段。下面以脚本代码段#6为例。3.编写代码块说明&#xff1a;编写javascript代码&#xff0c;点击下面的…

Spring Easy

Spring Easy 用途 通过自动配置&#xff0c;实现了一些国内 Spring Boot 开发时需要在 Spring Boot 框架基础上完成的一些配置工作&#xff0c;可以提升基于 Spring Boot 开发 Web 应用的效率。 安装 使用 Maven 进行包管理&#xff0c;可以从中央仓库安装依赖&#xff1a;…

【Node.js】文本与 pdf 的相互转换

pdf 转文本 主要使用 pdf-parse 这个库&#xff0c;直接识别提取我们 pdf 文件中的文字。 const express require("express"); const fs require("fs"); const PDFParser require("pdf-parse"); const cors require("cors");const…

分布式ID方案

目录 &#x1f4ca; 分布式ID方案核心指标对比 &#x1f50d; 分方案深度解析 ⚙️ 1. UUID (Universally Unique Identifier) ❄️ 2. Snowflake (Twitter开源) ☘️ 3. 美团Leaf 号段模式 Snowflake模式 &#x1f504; 4. 百度UidGenerator &#x1f680; 5. CosId …

张量类型转换

一.前言本章节我们来讲解张量的类型转换&#xff0c;掌握张量的转换方法&#xff0c;张量的类型转换也是经常使⽤的⼀种操作&#xff0c;是必须掌握的知识点。在本⼩节&#xff0c;我们主要学习如何将 numpy 数组和 PyTorch Tensor 的转化⽅法.二.张量转换为 numpy 数组使⽤ Te…

JavaEE-初阶-多线程初阶

概念第一个多线程程序 可以通过查看jdk路径来找到jdk的控制可以通过jconsole来查看线程。创建线程这是实现多线程的其中一种方法&#xff0c;继承Thread类&#xff0c;实现run方法&#xff0c;之后实例化继承了Thread类的MyThread方法&#xff0c;调用start方法&#xff0c;就会…

解释全连接层的“参数数量”和“计算过程”,保证像看动画片一样直观~

假设场景输入图像&#xff1a;一张极小的 灰度图&#xff08;即 H2,W2&#xff0c;共4个像素&#xff09;&#xff0c;像素值如图所示&#xff1a;隐藏层&#xff1a;假设隐藏层也是 &#xff08;即 H2,W2&#xff0c;共4个神经元&#xff09;&#xff0c;每个神经元用 ( 表示…

DOM编程实例(不重要,可忽略)

文章目录 简介 表格增加删除&#xff0c;效果如下图 样式属性案例 简介 DOM---表格添加删除&#xff0c;样式属性案例 表格增加删除&#xff0c;效果如下图 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><met…

​Windows API 介绍及核心函数分类表

Windows API 介绍​ Windows API&#xff08;Application Programming Interface&#xff09;&#xff0c;也称为WinAPI&#xff0c;是微软Windows操作系统的核心编程接口。它提供了一系列函数、消息、数据结构、宏和系统服务&#xff0c;允许开发者创建运行在Windows平台上的应…

Kubernetes Dashboard UI 部署安装

K8S 集群环境&#xff1a; Ubuntu 24 / K8S 1.28.21. 推荐使用helm 安装Kubernetes Dashboardsudo snap install helm --classic2. 部署Kubernetes Dashboard# Add kubernetes-dashboard repository helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboar…

python-enumrate函数

文章目录基本语法基本用法基本遍历指定起始索引实际应用场景需要索引的循环创建字典映射处理文件行号与range(len())对比注意事项enumerate()是Python内置函数&#xff0c;用于在遍历序列&#xff08;如列表、元组或字符串&#xff09;时同时获取索引和值。基本语法 enumerate…

FPGA通信设计十问

1. FFT有什么用&#xff1f;FFT&#xff08;快速傅里叶变换&#xff09;是离散傅里叶变换&#xff08;DFT&#xff09;的高效实现算法&#xff0c;它的核心作用是快速将信号从时域转换到频域&#xff0c;从而简化信号分析和处理的过程。自然界的信号&#xff08;如声音、图像、…

代理模式——Java

代理模式 在Java中代理模式是一种设计模式&#xff0c;是通过代理类来代替原始的对象&#xff0c;可以在不改变原始对象的基础上&#xff0c;对它进行扩展&#xff08;新增一些新功能&#xff09;。在目标方法的执行的执行前后添加一些自定义的方法。 静态代理 步骤&#xff1a…