【GO基础学习】gin的使用

news2025/1/5 16:10:19

文章目录

  • 模版使用流程
  • 参数传递
  • 路由分组
  • 数据解析和绑定
  • gin中间件


模版使用流程

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    // 1.创建路由
   r := gin.Default()
   // 2.绑定路由规则,执行的函数
   // gin.Context,封装了request和response
   r.GET("/", func(c *gin.Context) {
      c.String(http.StatusOK, "hello World!")
   })
   // 3.监听端口,默认在8080
   // Run("里面不指定端口号默认为8080") 
   r.Run(":8000")
}

运行,访问:http://localhost:8000/

在这里插入图片描述
后面为了减少累赘代码,只展示路由规则部分代码编写,创建路由,以及端口监听部分代码保存相同。


参数传递

  1. API传参:可以通过Context的Param方法来获取API参数。
r.GET("/user/:name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "Hello "+name+"!")
	})

在这里插入图片描述

r.GET("/user/:name/*action", func(c *gin.Context) {
		name := c.Param("name")
		action := c.Param("action")
		c.String(http.StatusOK, "Hello "+name+"!"+"Action "+action)
	})

在这里插入图片描述

*action 表示一个通配路径参数,可以匹配 URL 剩余部分的路径,并且会包含匹配部分的斜杠 /

  1. URL传参:URL参数可以通过DefaultQuery()Query()方法获取,DefaultQuery()若参数不存在,返回默认值,Query()若不存在,返回空串。

DefaultQuery默认值只支持string类型:

func (c *Context) DefaultQuery(key, defaultValue string) string {
	if value, ok := c.GetQuery(key); ok {
		return value
	}
	return defaultValue
}
r.GET("/user", func(c *gin.Context) {
		name := c.DefaultQuery("name", "Test")
		ageStr := c.DefaultQuery("age", "18")
		age, err := strconv.Atoi(ageStr)
		if err != nil {
			c.String(http.StatusBadRequest, "Invalid age")
			return
		}
		c.String(http.StatusOK, fmt.Sprintf("%s is %d years old", name, age))
	})

在这里插入图片描述

  1. 表单传值
<form action="http://localhost:8000/form" method="post" action="application/x-www-form-urlencoded">
        用户名:<input type="text" name="username" placeholder="请输入你的用户名">  <br>&nbsp;&nbsp;&nbsp;码:<input type="password" name="userpassword" placeholder="请输入你的密码">  <br>
        <input type="submit" value="提交">
    </form>
r.POST("/form", func(c *gin.Context) {
        types := c.DefaultPostForm("type", "post")
        username := c.PostForm("username")
        password := c.PostForm("userpassword")
        c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
    })
  1. 文件上传

单个文件:

<form action="http://localhost:8000/upload" method="post" enctype="multipart/form-data">
          上传文件:<input type="file" name="file" >
          <input type="submit" value="提交">
    </form>
// 限制上传文件大小 (10 MiB)
    r.MaxMultipartMemory = 10 << 20 // 10 MiB

    // 文件上传路由
    r.POST("/upload", func(c *gin.Context) {
        // 获取文件
        file, err := c.FormFile("file")
        if err != nil {
            c.String(http.StatusBadRequest, "File upload error: %s", err.Error())
            return
        }

        // 检查文件类型 (仅允许 image/png)
        mimeType := file.Header.Get("Content-Type")
        if mimeType != "image/png" {
            c.String(http.StatusBadRequest, "Invalid file type: %s. Only PNG images are allowed.", mimeType)
            return
        }

        // 保存文件到目标路径
        dst := filepath.Join("./uploads", file.Filename)
        if err := c.SaveUploadedFile(file, dst); err != nil {
            c.String(http.StatusInternalServerError, "Could not save file: %s", err.Error())
            return
        }

        c.String(http.StatusOK, "File uploaded successfully: %s", file.Filename)
    })

多个文件:

// 限制上传文件大小 (10 MiB)
    r.MaxMultipartMemory = 10 << 20 // 10 MiB

    // 多文件上传路由
    r.POST("/upload/multiple", func(c *gin.Context) {
        // 获取表单文件 (key 为 "files")
        form, err := c.MultipartForm()
        if err != nil {
            c.String(http.StatusBadRequest, "Form error: %s", err.Error())
            return
        }

        files := form.File["files"] // 获取多个文件
        for _, file := range files {
            // 检查文件类型 (仅允许 image/png)
            mimeType := file.Header.Get("Content-Type")
            if mimeType != "image/png" {
                c.String(http.StatusBadRequest, "Invalid file type: %s for file: %s. Only PNG images are allowed.", mimeType, file.Filename)
                return
            }

            // 保存文件到目标路径
            dst := filepath.Join("./uploads", file.Filename)
            if err := c.SaveUploadedFile(file, dst); err != nil {
                c.String(http.StatusInternalServerError, "Could not save file: %s. Error: %s", file.Filename, err.Error())
                return
            }

            fmt.Printf("Uploaded file: %s\n", file.Filename)
        }

        c.String(http.StatusOK, "All files uploaded successfully")
    })


路由分组

在 Gin 框架中,路由分组(Route Group)是用于组织和管理路由的功能。通过路由分组,可以为一组路由添加公共的前缀、中间件或配置,从而提高代码的可读性和可维护性。

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	// 1.创建路由
	r := gin.Default()
	// 2.绑定路由规则,执行的函数
	// gin.Context,封装了request和response
	// 用户路由组
	userGroup := r.Group("/user")
	{
		userGroup.GET("/login", login)
		userGroup.GET("/settings", func(c *gin.Context) {
			c.JSON(200, gin.H{"message": "User Settings"})
		})
	}

	// 管理员路由组
	adminGroup := r.Group("/admin")
	{
		adminGroup.GET("/dashboard", func(c *gin.Context) {
			c.JSON(200, gin.H{"message": "Admin Dashboard"})
		})
		adminGroup.GET("/reports", func(c *gin.Context) {
			c.JSON(200, gin.H{"message": "Admin Reports"})
		})
	}
	// 404
	r.NoRoute(func(c *gin.Context) {
		c.String(http.StatusNotFound, "404 not found !!!!")
	})

	// 3.监听端口,默认在8080
	// Run("里面不指定端口号默认为8080")
	r.Run(":8000")
}

func login(c *gin.Context) {
	name := c.DefaultQuery("name", "jack")
	c.JSON(200, gin.H{"name": name})
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


数据解析和绑定

  1. JSON数据解析和绑定的基本步骤

(1) 定义结构体

定义一个与 JSON 数据格式匹配的 Go 结构体。

(2) 使用 c.BindJSONc.ShouldBindJSON

  • c.BindJSON 会在绑定失败时返回错误并终止请求。
  • c.ShouldBindJSON 返回错误但不会中断后续处理,适合灵活的错误处理场景。

(3) 使用结构体处理数据

通过绑定的结构体直接访问解析后的字段。

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

type User struct {
    Name  string `json:"name" binding:"required"` // 必填字段
    Age   int    `json:"age" binding:"required"`  // 必填字段
    Email string `json:"email"`                  // 可选字段
}

func main() {
    r := gin.Default()

    r.POST("/user", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "message": "User data received",
            "user":    user,
        })
    })

    r.Run(":8080")
}

在这里插入图片描述

Gin 提供了 binding 标签,用于对字段进行验证。支持多种验证规则。

type User struct {
    Name  string `json:"name" binding:"required,min=3"` // 必填,最小长度 3
    Age   int    `json:"age" binding:"required,gte=1"`  // 必填,必须大于等于 1
    Email string `json:"email" binding:"required,email"` // 必填,必须是合法邮箱
}

验证规则含义
required必须提供该字段
min字符串或切片的最小长度
max字符串或切片的最大长度
gte数字或时间的值必须大于等于指定值
lte数字或时间的值必须小于等于指定值
email必须是有效的邮箱地址

  1. 表单数据:
    为字段添加多种绑定方式的标签,例如 jsonform 标签。

Gin 会根据 Content-Type 自动选择适合的绑定方式:

  • 如果是 application/json,则绑定 JSON 数据。
  • 如果是 application/x-www-form-urlencodedmultipart/form-data,则绑定表单数据。
type User struct {
    Name  string `json:"name" form:"name" binding:"required"` // 必填字段
    Age   int    `json:"age" form:"age" binding:"required,gte=0"`
    Email string `json:"email" form:"email"`
}

  1. URI数据

通过 c.Param 获取路径参数的值

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.GET("/user/:id", func(c *gin.Context) {
        id := c.Param("id") // 提取路径参数
        c.JSON(http.StatusOK, gin.H{
            "user_id": id,
        })
    })

    r.Run(":8080")
}

Gin 提供了 ShouldBindUri 方法,可以将路径参数直接绑定到结构体中,方便处理和验证。

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type UserRequest struct {
    ID string `uri:"id" binding:"required"` // 使用 `uri` 标签定义路径参数
}

func main() {
    r := gin.Default()

    r.GET("/user/:id", func(c *gin.Context) {
        var req UserRequest
        // 将路径参数绑定到结构体
        if err := c.ShouldBindUri(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "user_id": req.ID,
        })
    })

    r.Run(":8080")
}

  1. 路径参数可以和查询参数或表单参数结合使用。Gin 支持同时解析这些参数。
package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type UserRequest struct {
    ID   string `uri:"id" binding:"required"`  // 路径参数
    Name string `form:"name"`                 // 查询参数或表单参数
}

func main() {
    r := gin.Default()

    r.GET("/user/:id", func(c *gin.Context) {
        var req UserRequest

        // 绑定路径参数
        if err := c.ShouldBindUri(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        // 绑定查询参数
        if err := c.ShouldBindQuery(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "user_id": req.ID,
            "name":    req.Name,
        })
    })

    r.Run(":8080")
}

GET /user/123?name=Alice

gin中间件

在 Gin 中,中间件是一个可以插入到请求处理流程中的函数,用于实现请求前后的通用逻辑。例如,认证、日志记录、跨域处理等都可以通过中间件实现。

  1. 中间件定义
func(c *gin.Context) {
    // 执行逻辑
    c.Next() // 调用后续处理函数
    // 执行逻辑
}

c.Next():调用下一个中间件或处理程序。
c.Abort():停止执行后续的中间件和处理程序。

  1. 全局中间件

全局中间件会应用到所有路由,使用 r.Use() 注册。

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 请求前逻辑
		fmt.Println("Request URL:", c.Request.URL.Path)

		c.Next() // 执行后续处理

		// 请求后逻辑
		fmt.Println("Response Status:", c.Writer.Status())
	}
}

func main() {
	r := gin.Default()

	// 注册全局中间件
	r.Use(Logger())

	r.GET("/ping", func(c *gin.Context) {
		fmt.Println("=======11111111")
		c.String(200, "pong")
	})

	r.Run(":8080")
}

控制台输出:

Request URL: /ping
=======11111111
Response Status: 200

流程:中间件逻辑=》遇到next方法后,执行路由func=》中间件后面的逻辑。

  1. 局部中间件

局部中间件只应用于特定路由或路由组。

package main

import (
	"github.com/gin-gonic/gin"
)

func AuthRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("Authorization")
		if token != "valid-token" {
			c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
			return
		}
		c.Next()
	}
}

func main() {
	r := gin.Default()

	// 局部中间件
	r.GET("/secure", AuthRequired(), func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "Authorized"})
	})

	r.Run(":8080")
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

溯源取证-手机取证-简单篇

好久没有写了&#xff0c;水一篇简单的 案例摘要&#xff1a; 我们目前正在调查一起谋杀案&#xff0c;目前已经获得了受害者的手机作为关键证据。在与证人和受害者核心圈子相关人员进行面谈后&#xff0c;您的目标是分析我们收集的信息&#xff0c;并努力追踪证据&#xff0…

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(四)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 四、提示范式&#xff08;Explanation for Prompting Paradigm&#xff09; 随着语言模型规模的扩大&#xff0c;基于提示&#xff08;prom…

基于TCP的Qt网络通信

基于TCP的Qt网络通信 项目源码&#xff1a;https://github.com/say-Hai/TcpSocketLearn/tree/QTcpSocket 在标准C没有提供专门用于套接字通信的类&#xff0c;所以只能使用操作系统提供的基于C的API函数&#xff0c;但是Qt就不一样了&#xff0c;它是C的一个框架并且里边提供了…

java vscode springboot 问题汇总

问题1 VSCODE配置SpringBoot&#xff0c;创建新项目运行时报错程序包不存在 找不到符号解决方法 选择Run Java即可 , 不是Run Code DemoApplication.java:3: 错误: 程序包org.springframework.boot不存在 import org.springframework.boot.SpringApplication; …

BLIP论文笔记

论文地址 BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation 论文思想 其实Clip就相当于只用了ITC

linux上使用cmake编译的方法

一、hello 例程仅基于一个cpp文件 C文件或工程进行编译时可以使用g指令&#xff08;需要对每一个程序和源文件分别使用g指令编译&#xff09;&#xff0c;当程序变大时&#xff0c;一个工程文件往往会包含很文件夹和源文件&#xff0c;这时我们需要的编译指令将越来越长&#…

Speech Recognition vs. Voice Recognition | 语音识别工作原理 | 模型训练 | 应用

注&#xff1a;Speech Recognition 与 Voice Recognition 机翻混淆&#xff0c;未校。 Speech Recognition vs. Voice Recognition: In Depth Comparison 语音识别与语音识别&#xff1a;深度比较 Calendar12 July 2023 Have you ever stopped to think about how your voice…

Tushare提示本接口即将停止更新,请尽快使用Pro版接口:https://tushare.pro/document/2

1、解决办法 tushare pro版本需要注册登录后获取tocken然后在代码中配置接口即可。 2、如何获取tushare&#xff1f; tushare是免费、开源的python财经数据接口&#xff0c;现在使用需要先注册tushare账号 1、跳转到tushare注册页面 Tusharez注册 Tushare数据 2、注册成功…

高频 SQL 50 题(基础版)_1068. 产品销售分析 I

销售表 Sales&#xff1a; (sale_id, year) 是销售表 Sales 的主键&#xff08;具有唯一值的列的组合&#xff09;。 product_id 是关联到产品表 Product 的外键&#xff08;reference 列&#xff09;。 该表的每一行显示 product_id 在某一年的销售情况。 注意: price 表示每…

【Ubuntu】Ubuntu server 18.04 搭建Slurm并行计算环境(包含NFS)

Ubuntu server 18.04 搭建Slurm并行计算环境&#xff08;包含NFS&#xff09; 一、Munge 认证模块 1.1、安装 munge 主节点和子节点都安装munge #安装 sudo apt update && sudo apt install munge libmunge-dev#设置开机启动 sudo systemctl enable munge sudo syste…

电脑主机后置音频插孔无声?还得Realtek高清晰音频管理器调教

0 缘起 一台联想电脑&#xff0c;使用Windows 10 专业版32位&#xff0c;电脑主机后置音频插孔一直没有声音&#xff0c;所以音箱是接在机箱前面版的前置音频插孔上的。 一天不小心捱到了音箱的音频线&#xff0c;音频线头断在音频插孔里面了&#xff0c;前置音频插孔因此用不…

Flink源码解析之:如何根据算法生成StreamGraph过程

Flink源码解析之&#xff1a;如何根据算法生成StreamGraph过程 在我们日常编写Flink应用的时候&#xff0c;会首先创建一个StreamExecutionEnvironment.getExecutionEnvironment()对象&#xff0c;在添加一些自定义处理算子后&#xff0c;会调用env.execute来执行定义好的Flin…

矩阵运算提速——玩转opencv::Mat

介绍:用Eigen或opencv::Mat进行矩阵的运算&#xff0c;比用cpp的vector或vector进行矩阵运算要快吗? 使用 Eigen 或 OpenCV 的 cv::Mat 进行矩阵运算通常比使用 std::vector<int> 或 std::vector<double> 更快。这主要有以下几个原因&#xff1a; 优化的底层实现…

Spire.PDF for .NET【页面设置】演示:向 PDF 添加平铺背景图像

平铺背景通常是指用一个或多个小图像重复填充的背景。在本文中&#xff0c;您将学习如何在 PDF 中平铺图像&#xff0c;并使用 C# 和 VB.NET 为您的 PDF 创建平铺背景。 Spire.PDF for .NET 是一款独立 PDF 控件&#xff0c;用于 .NET 程序中创建、编辑和操作 PDF 文档。使用 …

文件本地和OSS上传

这里写目录标题 前端传出文件后端本地存储阿里云OSS存储上传Demo实现上传ConfigurationProperties 前端传出文件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上传文件</title> </head&g…

SpringCloud源码分析-Gateway

Gateway核心原理&#xff0c;请求经过一系列的责任链最后到达服务端。

两个等号和三个等号(待查资料)

1太癫了&#xff0c;居然时要才行&#xff0c;都不行 2. 三等号的结果 .。。。。毁灭吧

什么是神经网络?神经网络的基本组成部分训练神经网络激活函数有哪些局限性和挑战

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

《Java核心技术II》流中的filter、map和flatMap方法

filter、map和flatMap方法 filter filter通过转换产生过滤后的新流,将字符串流转化为只包含长单词的另一个流。 List words ...; Stream longWords words.stream().filter(w->w.length()>12) filter类型是Predicate(谓词&#xff0c;表示动作)类型对象&#xff0c…

linux进阶

目录 变量 shell变量 环境变量 预定义变量 位置变量 其他 管道与重定向 管道 重定向 shell脚本 分支结构 循环结构 数组 脚本实例 变量 shell变量 shell变量&#xff1a;shell程序在内存中存储数据的容器 shell变量的设置&#xff1a;colorred 将命令的结果赋值…