安装gin框架
go get -u github.com/gin-gonic/gin
这一步如果安装不了,需要在goland中设置代理,启用Go模块集成,并把环境设置为:GOPROXY=https://goproxy.io

并且设置GOPATH,勾选为整个GOPATH编制索引和使用系统环境中定义的GOPATH

设置完成后,重启goland
导入gin
import("github.com/gin-gonic/gin")
实例代码:
package main
import "github.com/gin-gonic/gin"
//web图标
import "github.com/thinkerou/favicon"
func main(){
//创建一个服务
ginServer := gin.Default()
//使用图标,图标可以是任何图片
ginServer.Use(favicon.New("./favicon.ico"))
//连接数据库的代码
//访问地址,处理请求 Request Response
//func(context *gin.Context) 定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
ginServer.GET("/hello",func(context *gin.Context){
context.JSON(200,gin.H{"msg": "hello.world"})
})
//服务器端口
ginServer.Run(":8080")
}
实例页面:

RESTful API
以前写网站
get /user
post /create_user
post /update_user
post /delete_user
使用RESTful API写网站
get /user
post /user
put /user
delete /user
gin框架支持RESTful API的开发。
实例代码:
package main
import "github.com/gin-gonic/gin"
//web图标
import "github.com/thinkerou/favicon"
func main() {
//创建一个服务
ginServer := gin.Default()
//使用图标,图标可以是任何图片
ginServer.Use(favicon.New("./favicon.ico"))
//连接数据库的代码
//访问地址,处理请求 Request Response
//func(context *gin.Context) 定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
ginServer.GET("/hello", func(context *gin.Context) {
context.JSON(200, gin.H{"msg": "hello.world"})
})
//Gin RestFul的编写方式
ginServer.POST("/user", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "post,user"})
})
ginServer.PUT("/user")
ginServer.DELETE("/user")
//服务器端口
ginServer.Run(":8080")
}

加载静态页面
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
//web图标
import "github.com/thinkerou/favicon"
func main() {
//创建一个服务
ginServer := gin.Default()
//使用图标,图标可以是任何图片
ginServer.Use(favicon.New("./favicon.ico"))
// 加载资源文件
ginServer.Static("/static","./static")
//加载静态页面
ginServer.LoadHTMLGlob("templates/*")
//响应一个页面给前端
ginServer.GET("/user",func(context *gin.Context){
//context.JSON()
context.HTML(http.StatusOK,"index.html",gin.H{"msg": "测试"})
})
//连接数据库的代码
//访问地址,处理请求 Request Response
//func(context *gin.Context) 定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
ginServer.GET("/hello", func(context *gin.Context) {
context.JSON(200, gin.H{"msg": "hello.world"})
})
//Gin RestFul的编写方式
ginServer.POST("/user", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "post,user"})
})
ginServer.PUT("/user")
ginServer.DELETE("/user")
//服务器端口
ginServer.Run(":8080")
}

获取前端传入的数据
package main
import (
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
)
//web图标
import "github.com/thinkerou/favicon"
func main() {
//创建一个服务
ginServer := gin.Default()
//使用图标,图标可以是任何图片
ginServer.Use(favicon.New("./8.png"))
// 加载资源文件
ginServer.Static("/static", "./static")
//加载静态页面
ginServer.LoadHTMLGlob("templates/*")
//响应一个页面给前端
ginServer.GET("/user", func(context *gin.Context) {
//context.JSON()
context.HTML(http.StatusOK, "index.html", gin.H{"msg": "测试"})
})
//连接数据库的代码
//访问地址,处理请求 Request Response
//func(context *gin.Context) 定义匿名函数处理请求,参数“context”是Gin框架中封装了HTTP请求和响应的上下文对象。
ginServer.GET("/hello", func(context *gin.Context) {
context.JSON(200, gin.H{"msg": "hello.world"})
})
//Gin RestFul的编写方式
ginServer.POST("/user", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "post,user"})
})
ginServer.PUT("/user")
ginServer.DELETE("/user")
//获取前端来自的数据
ginServer.GET("user/info", func(context *gin.Context) {
userid := context.Query("userid")
username := context.Query("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
})
})
//另一种方式
ginServer.GET("user/info/:userid/:username", func(context *gin.Context) {
userid := context.Param("userid")
username := context.Param("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
})
})
//POST传参json
ginServer.POST("/json", func(context *gin.Context) {
//request.body
//[]byte
data, _ := context.GetRawData()
var m map[string]interface{}
// 包装为json数据 []byte
_ = json.Unmarshal(data, &m)
context.JSON(http.StatusOK, m)
})
//POST传参form
ginServer.POST("/form/login", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
context.JSON(http.StatusOK, gin.H{
"msg": "登录成功",
"username": username,
"password": password,
})
})
//服务器端口
ginServer.Run(":8080")
}



重定向
gitServer.GET("/test123",func(context *gin.Context){
context.Redirect(http.StatusMovedPermanently,"https://www.nightying.com")
}
错误页面
ginServer.NoRoute(func(context *gin.Context){
context.HTML(http.StatusNotFound,"404.html",nil)
})
路由组(蓝图)
userGroup := ginServer.Group("/user")
{
userGroup.GET("/add")
userGroup.GET("/login")
userGroup.GET("/logout")
}
order := ginServer.Group("/order")
{
orderGroup.GET("/add")
orderGroup.DELETE("/delte")
}
拦截器 – 中间件
(一般用于预处理数据,比如登录,分页,
//自定义中间件 拦截器
func myHandler()(gin.HandlerFunc){
return func(context *gin.Context){
//通过自定义的中间件,设置的值,在后续处理只要调用了这个中间件的都可以拿到这里的参数
context.Set("usersesion","userid-1")
context.Next() //放行
context.Abort() //阻止
}
}
使用方式:
gitServer.GET("/user/add",myHandler(),func(context *gin.Context){
//取出中间件的值
usersesion := context.MustGet("usersesion").(string)
log.Println(userSession)
}
go语言多线程
package main
import "fmt"
func hhh(){
fmt.Println("测")
}
func main(){
go hhh()
fmt.Println("main")
}
项目实例
(该项目是GDX_helium雏形)
项目结构:
C:.
│ .gitignore //github上传限制
│ buildsql.go //新建数据库
│ go.mod //包导入
│ go.sum
│ main.go //主函数
│ README.md
│ setsql.go //测试数据
│ 结构.txt
│
├─.idea
│ .gitignore
│ .name
│ GDX_helium.iml
│ modules.xml
│ vcs.xml
│ workspace.xml
│
├─api //api文档
│ └─v1
│ ├─admin
│ │ admin.go
│ │
│ ├─client
│ ├─login
│ │ login.go
│ │
│ ├─quest
│ └─template
├─conf //web相关配置文件
│ config.go
│ config.yaml
│
├─const //常量
│ enum.go //枚举文件
│
├─db
│ db.go //数据库
│
├─log
│ logger.go //日志
│
├─models //gorm模型
│ user.go
│
├─static //静态文件
├─system //系统相关
│ init.go //设置
│
├─templates //模板
└─utils //复用文件
file.go
主框架main.go:
package main
import (
"GDX_helium/api/v1/admin"
"GDX_helium/conf"
"GDX_helium/db"
"GDX_helium/log"
"GDX_helium/system"
"github.com/gin-gonic/gin"
)
func main() {
// 初始化配置
conf.Init()
// 初始化日志
log.Init()
// 初始化系统
system.Init()
// 初始化数据库
db.Init()
// 创建Gin引擎
r := gin.Default()
// 注册路由
v1 := r.Group("/api/v1")
{
admin.RegisterRoutes(v1)
}
// 启动服务器
r.Run(":8080")
}
数据库文件db.go
package db
import (
"GDX_helium/conf"
user "GDX_helium/models"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
var DB *gorm.DB
func Buildsql() { //生成数据库
err := DB.AutoMigrate(&user.User{}) // 使用 user.User新建user表
if err != nil {
log.Fatalf("迁移数据库出错: %v", err)
}
}
func Init() {
dbConfig := conf.GetDatabaseConfig() //导入数据库配置信息
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
dbConfig["user"], dbConfig["password"], dbConfig["host"], dbConfig["port"], dbConfig["name"])
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) //连接数据库
if err != nil {
log.Fatalf("连接数据库错误: %v", err)
}
}
配置文件config.go
package conf
import (
"github.com/spf13/viper"
"log"
)
func Init() {
// 设置配置文件名 (不包括扩展名)
viper.SetConfigName("config")
// 设置配置文件类型
viper.SetConfigType("yaml")
// 设置配置文件路径,可以设置多个路径
viper.AddConfigPath("./conf")
// 读取配置文件
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("Error reading config file, %s", err)
}
}
// 获取配置项
func GetServerPort() string { //端口
return viper.GetString("server.port")
}
func GetDatabaseConfig() map[string]string { //获取数据库相关配置
return map[string]string{
"type": viper.GetString("database.type"),
"host": viper.GetString("database.host"),
"port": viper.GetString("database.port"),
"user": viper.GetString("database.user"),
"password": viper.GetString("database.password"),
"name": viper.GetString("database.name"),
}
}
配置文件config.yaml(主要是参考格式)
# 应用程序配置
app:
name: GDX_helium
mode: debug # release
# 服务器端口
server:
port: 8080
# 数据库
database:
type: mysql
host: localhost
port: 3306
user: root
password: 123456
name: gdx_helium
# 日志
log:
level: info
format: text # or json
# JWT配置
jwt:
secret: key
expire: 3600 # in seconds
# 缓存
cache:
type: redis
host: localhost
port: 6379
password: ""
# 邮箱
email:
smtp_host: smtp.example.com
smtp_port: 587
user: [email protected]
password: email_password
枚举函数enum.go
package enum
import (
"database/sql/driver"
"errors"
)
type UserRole string //用户角色
const (
Admin UserRole = "admin" //管理员
User UserRole = "user" //用户
Guest UserRole = "block" //冻结
)
func (u *UserRole) Scan(value interface{}) error { //枚举通用
if value == nil {
*u = ""
return nil
}
str, ok := value.([]byte)
if !ok {
return errors.New("UserRole的数据无效")
}
*u = UserRole(str)
return nil
}
func (u UserRole) Value() (driver.Value, error) { //枚举通用
return string(u), nil
}
type OrderStatus string
const (
Pending OrderStatus = "pending"
Completed OrderStatus = "completed"
Canceled OrderStatus = "canceled"
)
func (o *OrderStatus) Scan(value interface{}) error {
if value == nil {
*o = ""
return nil
}
str, ok := value.([]byte)
if !ok {
return errors.New("OrderStatus的无效数据")
}
*o = OrderStatus(str)
return nil
}
func (o OrderStatus) Value() (driver.Value, error) {
return string(o), nil
}
// IsValid 检查状态是否有效
func (s OrderStatus) IsValid() bool {
switch s {
case pending, running, ended, block:
return true
default:
return false
}
}
数据库ORM模型-user.go
package user
import (
enum "GDX_helium/const"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
type User struct {
ID uint `gorm:"primaryKey"` //用户ID
Name string `gorm:"size:100;not null" json:"name"` //用户昵称
Email string `gorm:"size:100;uniqueIndex;null" json:"email"` //用户邮箱
LoginName string `gorm:"size:100;not null" json:"login_name"` //登陆账号
Password string `gorm:"size:100;not null" json:"password"` //密码
Phone string `gorm:"size:20;null" json:"phone"` //手机号
Role enum.UserRole `gorm:"type:enum('admin', 'user', 'block');default:'user'" json:"role"` //权限
}
// 对比密码
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
return err == nil
}
// 寻找用户
func GetUserByLoginName(loginName string, db *gorm.DB) (*User, error) {
var user User
if err := db.Where("login_name = ?", loginName).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
GORM模型关联
type TaskUser struct {
TaskID uint `gorm:"not null" json:"task_id"`
Task Task `gorm:"foreignKey:TaskID;references:TaskID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"task"`
UserID uint `gorm:"not null" json:"user_id"`
User User `gorm:"foreignKey:UserID;references:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"user"`
}
//foreignKey:设置外键,references:与对应的表中的字段关联
登录login.go
package admin
import (
"GDX_helium/db"
user "GDX_helium/models"
"github.com/gin-gonic/gin"
"net/http"
)
type LoginRequest struct {
LoginName string `json:"login_name" binding:"required"`
Password string `json:"password" binding:"required"`
}
func RegisterRoutes(r *gin.RouterGroup) {
r.GET("/admin", GetActivity)
r.POST("/login", Login)
}
func GetActivity(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Activity endpoint",
})
}
func Login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
return
}
user, err := user.GetUserByLoginName(req.LoginName, db.DB) //查询
if err != nil { //如果err不为nil
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
return
}
if !user.CheckPassword(req.Password) { //如果密码不一致
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
return
}
// 生成并返回 JWT 或其他会话管理方案
c.JSON(http.StatusOK, gin.H{
"message": "Login成功",
"user_id": user.ID,
"user_name": user.Name,
"role": user.Role.Value,
})
}
向数据库添加数据
func add_task(c *gin.Context) {
var req task_add
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
return
}
userID := c.MustGet("userID").(uint)
task := models.Task{
Introduce: req.Introduce,
CreatorID: userID,
BuildTime: time.Now().Unix(),
}
if err := db.DB.Create(&task).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建任务"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "任务创建成功", "task": task})
}
修改数据
var task models.Task
// 查找任务
if err := db.DB.First(&task, taskID).Error; err != nil {
if gorm.ErrRecordNotFound == err {
c.JSON(http.StatusNotFound, gin.H{"error": "任务未找到"})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法查找任务"})
}
return
}
// 更新任务字段
task.Introduce = req.Introduce
task.Frequency = req.Frequency
task.Status = req.Status
// 保存更新
if err := db.DB.Save(&task).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法更新任务"})
return
}
查询数据库
// GetTaskList 查询所有任务列表 Find查询所有列表
func AdminGetTaskList(db *gorm.DB) ([]Task, error) {
var tasks []Task
if err := db.Find(&tasks).Error; err != nil {
return nil, err
}
return tasks, nil
}
//查询单个数据 Where().First()
// GetUserByLoginName通过登录名检索用户
func GetUserByLoginName(loginName string, db *gorm.DB) (*User, error) {
var user User
if err := db.Where("login_name = ?", loginName).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
//联表查询 Joins().Where().Find()
//Joins联合查询
//JOIN task_users ON task_users.task_id = tasks.task_id
//联合task_users表,获取条件为task_users表的task_id预tasks表单task_id相等的数据
//Where筛选数据
//"task_users.user_id = ?",userID
//筛选出task_users.user_id等于传入参数的userID的数据
//Find(&tasks)
//将数据存储至tasks中
func UserGetTaskList(db *gorm.DB, userID uint) ([]Task, error) {
var tasks []Task
// 通过中间表 task_user 查询指定用户的任务列表
if err := db.Joins("JOIN task_users ON task_users.task_id = tasks.task_id").
Where("task_users.user_id = ?", userID).
Find(&tasks).Error; err != nil {
return nil, err
}
return tasks, nil
}
JWT的使用
先安装jwt的包:
go get github.com/dgrijalva/jwt-go
JWT创建、生成、验证令牌:
package common
import (
"GDX_helium/conf"
"errors"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
"strings"
"time"
)
type Claims struct { // JWT结构
UserID uint `json:"user_id"` // 用户ID
Role string `json:"role"` // 用户角色
jwt.StandardClaims // 时间
}
// 生成JWT令牌
func GenerateJWT(userID uint, role string) (string, error) {
JWTConfig := conf.GetJWTConfig() // 导入JWT配置文件
var jwtKey = []byte(JWTConfig["JWTkey"])
expire, err := strconv.Atoi(JWTConfig["expire"])
expirationTime := time.Now().Add(time.Duration(expire) * time.Second)
claims := &Claims{
UserID: userID,
Role: role,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
return "", err
}
return tokenString, nil
}
// 验证JWT令牌
func ValidateJWT(tokenString string) (*Claims, error) {
JWTConfig := conf.GetJWTConfig() // 导入JWT配置文件
var jwtKey = []byte(JWTConfig["JWTkey"])
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorExpired != 0 {
return nil, errors.New("令牌已过期")
}
}
return nil, err
}
if !token.Valid {
return nil, errors.New("令牌无效")
}
return claims, nil
}
JWT拦截器(中间件):
func JWTAuthMiddleware() gin.HandlerFunc { //JWT中间件
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "认证失败"})
c.Abort()
return
}
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusUnauthorized, gin.H{"error": "认证失败"})
c.Abort()
return
}
claims, err := ValidateJWT(parts[1])
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的令牌"})
c.Abort()
return
}
c.Set("userID", claims.UserID)
c.Set("userRole", claims.Role)
c.Next()
}
JWT的使用:
修改main.go:
package main
import (
"GDX_helium/api/v1/admin"
"GDX_helium/common"
"GDX_helium/conf"
"GDX_helium/db"
"GDX_helium/log"
"GDX_helium/system"
"github.com/gin-gonic/gin"
)
func main() {
// 初始化配置
conf.Init()
// 初始化日志
log.Init()
// 初始化系统
system.Init()
// 初始化数据库
db.Init() // 新增此行
// 创建Gin引擎
r := gin.Default()
// 注册公共路由
public := r.Group("/api/v1")
admin.RegisterPublicRoutes(public)
// 注册受保护的路由
protected := r.Group("/api/v1")
protected.Use(common.JWTAuthMiddleware())
admin.RegisterProtectedRoutes(protected)
// 启动服务器
r.Run(":8080")
}
登录login.go:
func Login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBind(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
return
}
user, err := models.GetUserByLoginName(req.LoginName, db.DB)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
return
}
if !user.CheckPassword(req.Password) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的登录名或密码"})
return
}
// 生成JWT令牌
token, err := common.GenerateJWT(user.ID, string(user.Role))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法生成令牌"})
return
}
// 设置JWT令牌在Authorization头中
c.Header("Authorization", "Bearer "+token)
// 返回JWT令牌
c.JSON(http.StatusOK, gin.H{
"message": "登录成功",
"user_id": user.ID,
"user_name": user.Name,
"role": user.Role,
})
}
刷新token:
jwt.go:
// 检查令牌是否即将过期并生成新的令牌
func RefreshJWT(tokenString string) (string, error) {
claims, err := ValidateJWT(tokenString)
if err != nil {
return "", err
}
// 检查令牌是否即将过期,设置一个合理的时间窗口
if time.Until(time.Unix(claims.ExpiresAt, 0)) > 30*time.Minute {
return "", errors.New("令牌未接近过期,无需刷新")
}
// 生成新的令牌
return GenerateJWT(claims.UserID, claims.Role)
}
login.go:
func RefreshToken(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "请求头中缺少Authorization字段"})
return
}
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusUnauthorized, gin.H{"error": "请求头中的Authorization格式有误"})
return
}
newToken, err := common.RefreshJWT(parts[1])
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "令牌刷新成功",
"token": newToken,
})
}
在路由中使用:
func RegisterPublicRoutes(r *gin.RouterGroup) {
r.POST("/login", Login)
}
func RegisterProtectedRoutes(r *gin.RouterGroup) {
r.GET("/test", test)
}
//测试
func test(c *gin.Context) {
userID := c.MustGet("userID").(uint) //提取user_id
userRole := c.MustGet("userRole").(string) //提取角色
c.JSON(http.StatusOK, gin.H{
"message": "你已成功访问受保护的路由",
"user_id": userID,
"user_role": userRole,
})
}