【Go学习】04-1-Gin框架-路由请求响应参数

news2025/3/14 17:26:04

【Go学习】04-1-Gin框架

  • 初识框架
    • go流行的web框架
      • Gin
      • iris
      • Beego
      • fiber
    • Gin介绍
    • Gin快速入门
  • 路由
    • RESTful API规范
    • 请求方法
    • URI
      • 静态url
      • 路径参数
      • 模糊匹配
    • 处理函数
    • 分组路由
  • 请求参数
    • GET请求参数
      • 普通参数
      • 数组参数
      • map参数
    • POST请求参数
      • 表单参数
      • JSON参数
    • 路径参数
    • 文件参数
  • 响应
    • 字符串方式
    • JSON方式
    • XML方式
    • 文件方式
    • 设置http响应头
    • 重定向
    • YAML方式


初识框架

框架是一系列工具的集合,能让开发变的便捷。

学习框架的目的就是为了提供项目的开发效率,使我们更加专注业务,而不是和业务无关的底层代码。

go流行的web框架

如果学习过其他语言,可能知道Java用的比较多的是Spring框架,PHP用的比较多的是Laravel,python用的多的是Django,都在各自的语言中具有强大的统治力。

go从诞生之初就带有浓重的开源属性,其原生库已经很强大,即使不依赖框架,也能进行高性能开发,又因为其语言并没有一定的设计标准,所以较为灵活,也就诞生了众多的框架,各具有特色,满足不同的喜好。

Gin

地址:https://github.com/gin-gonic/gin

号称最快的go语言web框架,目前是go官方的推荐框架(https://go.dev/doc/tutorial/)。

iris

地址:https://github.com/kataras/iris

性能比gin高一些,支持MVC,但这款框架评价不太好,使用上问题较多,近些年很少去选择使用

Beego

地址:https://github.com/beego/beego

国人开发,最早的go web框架之一,工具集比较完善,性能较差,据传言作者是php转行,所以框架带有浓厚的php特色,早期国内使用的多,目前少有人选择。

fiber

地址:https://github.com/gofiber/fiber

2020年发布的框架,发展迅速,建立在fasthttp之上,性能目前最高,受Express启发,比较简洁,上手较快,和gin类似。

当然还有其他一些框架,但从star数上,以及流行程度上看,gin一骑绝尘,gin的好处在于其简洁,扩展性,稳定性以及性能都比较出色。

go的框架其实是可以理解为库,并不是用了某一个框架就不能用别的框架,可以选择性的使用各个库中的优秀组件,进行组合

Gin介绍

特性:

  • 快速

    基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。

  • 支持中间件

    传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。

  • Crash 处理

    Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!

  • JSON 验证

    Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。

  • 路由组

    更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。

  • 错误管理

    Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。

  • 内置渲染

    Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。

  • 可扩展性

    新建一个中间件非常简单。

Gin快速入门

go版本需求:go1.13及以上

环境:windows 11

# 创建工作区
F:\Code\Golang\TuLing\workPath>mkdir ginlearn
F:\Code\Golang\TuLing\workPath>cd ginlearn
# 初始化工作区
F:\Code\Golang\TuLing\workPath\ginlearn>go work init

# 创建模块
F:\Code\Golang\TuLing\workPath\ginlearn>mkdir helloworld
F:\Code\Golang\TuLing\workPath\ginlearn>cd helloworld
# 初始化模块
F:\Code\Golang\TuLing\workPath\ginlearn\helloworld>go mod init test.com/helloworld
go: creating new go.mod: module test.com/helloworld
F:\Code\Golang\TuLing\workPath\ginlearn\helloworld>cd ..

# 将模块加入工作区
F:\Code\Golang\TuLing\workPath\ginlearn>go work use ./helloworld

使用goland打开

在这里插入图片描述

下载gin

PS F:\Code\Golang\TuLing\workPath\ginlearn> cd .\helloworld\
PS F:\Code\Golang\TuLing\workPath\ginlearn\helloworld> go get -u github.com/gin-gonic/gin

示例程序,创建main.go

package main

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

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}

运行后,apifox进行测试

在这里插入图片描述

符合预期,这样简单的代码就实现了一个http的服务

路由

路由是URI到函数的映射。

一个URI含: http://localhost:8080/user/find?id=11

  • 协议,比如http,https等
  • ip端口或者域名,比如127.0.0.1:8080或者www.test.com
  • path,比如 /path
  • query,比如 ?query

同时访问的时候,还需要指明HTTP METHOD,比如

  • GET

    GET方法请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据.

  • POST

    POST方法用于将实体提交到指定的资源,通常会导致在服务器上的状态变化

  • HEAD

    HEAD方法请求一个与GET请求的响应相同的响应,但没有响应体.

  • PUT

    PUT方法用请求有效载荷替换目标资源的所有当前表示

  • DELETE

    DELETE方法删除指定的资源

  • CONNECT

    CONNECT方法建立一个到由目标资源标识的服务器的隧道。

  • OPTIONS

    OPTIONS方法用于描述目标资源的通信选项。

  • TRACE

    TRACE方法沿着到目标资源的路径执行一个消息环回测试。

  • PATCH

    PATCH方法用于对资源应用部分修改。

使用的时候,应该尽量遵循其语义

RESTful API规范

RESTful API 的规范建议我们使用特定的HTTP方法来对服务器上的资源进行操作。

比如:

  1. GET,表示读取服务器上的资源
  2. POST,表示在服务器上创建资源
  3. PUT,表示更新或者替换服务器上的资源
  4. DELETE,表示删除服务器上的资源
  5. PATCH,表示更新/修改资源的一部分

请求方法

r.GET("/get", func(ctx *gin.Context) {
	ctx.JSON(200, "get")
})
r.POST("/post", func(ctx *gin.Context) {
	ctx.JSON(200, "post")
})
r.DELETE("/delete", func(ctx *gin.Context) {
	ctx.JSON(200, "delete")
})
r.PUT("/put", func(ctx *gin.Context) {
	ctx.JSON(200, "put")
})

如果想要支持所有:

r.Any("/any", func(ctx *gin.Context) {
    ctx.JSON(200, "any")
})

如果想要支持其中的几种:

r.GET("/hello", func(ctx *gin.Context) {
    //数组 map list 结构体
    ctx.JSON(200, gin.H{
       "name": "hello world",
    })
})
r.POST("/hello", func(ctx *gin.Context) {
    //数组 map list 结构体
    ctx.JSON(200, gin.H{
       "name": "hello world",
    })
})

URI

URI书写的时候,我们不需要关心scheme和authority这两部分,我们主要通过path和query两部分的书写来进行资源的定位。

静态url

比如/hello/user/find

r.POST("/user/find", func(ctx *gin.Context) {
})

路径参数

比如/user/find/:id

r.POST("/user/find/:id", func(ctx *gin.Context) {
    param := ctx.Param("id")
    ctx.JSON(200, param)
})

模糊匹配

比如/user/*path

r.POST("/user/*path", func(ctx *gin.Context) {
		param := ctx.Param("path")
		ctx.JSON(200, param)
})

处理函数

定义:

type HandlerFunc func(*Context)

通过上下文的参数,获取http的请求参数,响应http请求等。

分组路由

在进行开发的时候,我们往往要进行模块的划分,比如用户模块,以user开发,商品模块,以goods开头。

或者进行多版本开发,不同版本之间路径是一致的,这种时候,就可以用到分组路由了。

比如

ug := r.Group("/user")
{
    ug.GET("find", func(ctx *gin.Context) {
       ctx.JSON(200, "user find")
    })
    ug.POST("save", func(ctx *gin.Context) {
       ctx.JSON(200, "user save")
    })
}
gg := r.Group("/goods")
{
    gg.GET("find", func(ctx *gin.Context) {
       ctx.JSON(200, "goods find")
    })
    gg.POST("save", func(ctx *gin.Context) {
       ctx.JSON(200, "goods save")
    })
}

请求路径则为

[GIN-debug] GET    /user/find                --> main.main.func2 (3 handlers)
[GIN-debug] POST   /user/save                --> main.main.func3 (3 handlers)
[GIN-debug] GET    /goods/find               --> main.main.func4 (3 handlers)
[GIN-debug] POST   /goods/save               --> main.main.func5 (3 handlers)

请求参数

GET请求参数

使用Get请求传参时,类似于这样

http://localhost:8080/user/save?id=11&name=zhangsan

如何获取呢?

普通参数

request url: http://localhost:8080/user/save?id=11&name=zhangsan

  • Query:匹配字段

    r.GET("/user/save", func(ctx *gin.Context) {
        id := ctx.Query("id")
        name := ctx.Query("name")
        ctx.JSON(200, gin.H{
           "id":   id,
           "name": name,
        })
    })
    

    如果参数不存在,就给一个默认值:

  • DefaultQuery:query为空时回返回个默认值

    r.GET("/user/save", func(ctx *gin.Context) {
        id := ctx.Query("id")
        name := ctx.Query("name")
        address := ctx.DefaultQuery("address", "北京")
        ctx.JSON(200, gin.H{
           "id":      id,
           "name":    name,
           "address": address,
        })
    })
    
  • GetQuery:多了个query成功与否的返回值

    r.GET("/user/save", func(ctx *gin.Context) {
        id, ok := ctx.GetQuery("id")
        address, aok := ctx.GetQuery("address")
        ctx.JSON(200, gin.H{
           "id":      id,
           "idok":    ok,
           "address": address,
           "aok":     aok,
        })
    })
    

    id是数值类型,上述获取的都是string类型,根据类型获取:通过form进行字段匹配

  • BindQuery:与结构体字段进行匹配

    type User struct {
    	Id   int64  `form:"id"`
    	Name string `form:"name"`
    }
    r.GET("/user/save", func(ctx *gin.Context) {
    		var user User
    		err := ctx.BindQuery(&user)
    		if err != nil {
    			log.Println(err)
    		}
    		ctx.JSON(200, user)
    })
    
  • ShouldBindQuery:有binding字段的要求必填,否则报错

    r.GET("/user/save", func(ctx *gin.Context) {
        var user User
        err := ctx.ShouldBindQuery(&user)
        if err != nil {
           log.Println(err)
        }
        ctx.JSON(200, user)
    })
    

    区别

    当bind是必须的时候,ShouldBindQuery会报错,开发者自行处理,状态码不变。

    type User struct {
        Id      int64  `form:"id"`
        Name    string `form:"name"`
        Address string `form:"address" binding:"required"`
    }
    

    BindQuery则报错的同时,会将状态码改为400。所以一般建议是使用Should开头的bind。

数组参数

请求url:http://localhost:8080/user/save?address=Beijing&address=shanghai

  • QueryArray:重复查询字段组装成数组

    r.GET("/user/save", func(ctx *gin.Context) {
        address := ctx.QueryArray("address")
        ctx.JSON(200, address)
    })
    
  • GetQueryArray:多成功与否返回值

    r.GET("/user/save", func(ctx *gin.Context) {
        address, ok := ctx.GetQueryArray("address")
        fmt.Println(ok)
        ctx.JSON(200, address)
    })
    
  • ShouldBindQuery

    r.GET("/user/save", func(ctx *gin.Context) {
        var user User
        err := ctx.ShouldBindQuery(&user)
        fmt.Println(err)
        ctx.JSON(200, user)
    })
    

    但是这样的话我们的user的address要求就是个数组

    type User struct {
        Id      int64    `form:"id"`
        Name    string   `form:"name"`
        Address []string `form:"address" binding:"required"`
    }
    

    成功返回

    {
        "Id": 0,
        "Name": "",
        "Address": [
            "Beijing",
            "shanghai"
        ]
    }
    

map参数

请求url:http://localhost:8080/user/save?addressMap[home]=Beijing&addressMap[company]=shanghai

  • QueryMap:组装成map

    r.GET("/user/save", func(ctx *gin.Context) {
        addressMap := ctx.QueryMap("addressMap")
        ctx.JSON(200, addressMap)
    })
    
  • GetQueryMap:多成功与否返回值

    r.GET("/user/save", func(ctx *gin.Context) {
        addressMap, _ := ctx.GetQueryMap("addressMap")
        ctx.JSON(200, addressMap)
    })
    

    返回值

    {
        "company": "shanghai",
        "home": "Beijing"
    }
    

POST请求参数

post请求一般是表单参数和json参数

表单参数

r.POST("/user/save", func(ctx *gin.Context) {
    id := ctx.PostForm("id")
    name := ctx.PostForm("name")
    address := ctx.PostFormArray("address")
    addressMap := ctx.PostFormMap("addressMap")
    ctx.JSON(200, gin.H{
       "id":         id,
       "name":       name,
       "address":    address,
       "addressMap": addressMap,
    })
})
  • PostForm:从表单中对应的字段
  • PostFormArray:从表单找对应的数组
  • PostFormMap:从表单找对应的Map
r.POST("/user/save", func(ctx *gin.Context) {
    var user User
    err := ctx.ShouldBind(&user)
    addressMap, _ := ctx.GetPostFormMap("addressMap")
    user.AddressMap = addressMap
    fmt.Println(err)
    ctx.JSON(200, user)
})
  • GetPostFormMap:从表单找对应的Map

JSON参数

json参数如下

{
    "id":1111,
    "name":"zhangsan",
    "address": [
        "beijing",
        "shanghai"
    ],
    "addressMap":{
        "home":"beijing"
    }
}
r.POST("/user/save", func(ctx *gin.Context) {
    var user User
    err := ctx.ShouldBindJSON(&user)
    fmt.Println(err)
    ctx.JSON(200, user)
})

对应字段进行匹配

其他类型参数注入xml,yaml等和json道理一样

路径参数

请求url:http://localhost:8080/user/save/111

r.POST("/user/save/:id", func(ctx *gin.Context) {
    ctx.JSON(200, ctx.Param("id"))
})
  • :id 表示 占位符,可以匹配 任意路径中的值

  • ctx.Param("id") 用于 获取路径参数 id 的值

文件参数

r.POST("/user/save", func(ctx *gin.Context) {
    form, err := ctx.MultipartForm()
    if err != nil {
       log.Println(err)
    }
    files := form.File
    for _, fileArray := range files {
       for _, v := range fileArray {
          ctx.SaveUploadedFile(v, "./"+v.Filename)
       }

    }
    ctx.JSON(200, form.Value)
})

在form表单中请求file类型

在这里插入图片描述

这样就能在本地看到了

在这里插入图片描述

响应

字符串方式

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.String(http.StatusOK, "this is a %s", "ms string response")
})

JSON方式

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.JSON(http.StatusOK, gin.H{
       "success": true,
    })
})

XML方式

r.GET("/user/save", func(ctx *gin.Context) {
    u := XmlUser{
       Id:   11,
       Name: "zhangsan",
    }
    ctx.XML(http.StatusOK, u)
})

文件方式

r.GET("/user/save", func(ctx *gin.Context) {
    //ctx.File("./1.png")
    ctx.FileAttachment("./1.png", "2.png")
})

设置http响应头

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.Header("test", "headertest")
})

重定向

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
})

YAML方式

r.GET("/user/save", func(ctx *gin.Context) {
    ctx.YAML(200, gin.H{"name": "ms", "age": 19})
})

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

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

相关文章

数据类设计_图片类设计之5_不规则类图形混合算法(前端架构)

前言 学的东西多了,要想办法用出来.C和C是偏向底层的语言,直接与数据打交道.尝试做一些和数据方面相关的内容 引入 接续上一篇,讨论图片类型设计出来后在场景中如何表达,以及不规则图片的混合算法. 图片示意图 图片是怎样表示的,这里把前面的补上 这里的数字1是不规则数据类对…

【模拟CMOS集成电路设计】带隙基准(Bandgap)设计与仿真(基于运放的电流模BGR)

【模拟CMOS集成电路设计】带隙基准(Bandgap)设计与仿真 前言工程文件&部分参数计算过程,私聊~ 一、 设计指标指标分析: 二、 电路分析三、 仿真3.1仿真电路图3.2仿真结果(1)运放增益(2)基准温度系数仿真(3)瞬态启动仿真(4)静态…

版本控制器Git(4)

文章目录 前言一、分布式版本控制系统的概念二、克隆远程仓库三、多用户协作与公钥管理四、配置Git忽略特殊文件五、给命令配置别名总结 前言 加油加油,路在脚下!!! 一、分布式版本控制系统的概念 本地操作:所有操作&a…

Rabbitmq--延迟消息

13.延迟消息 延迟消息:生产者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才会收到消息 延迟任务:一定时间之后才会执行的任务 1.死信交换机 当一个队列中的某条消息满足下列情况之一时,就会…

springboot436-基于SpringBoot的汽车票网上预订系统(源码+数据库+纯前后端分离+部署讲解等)

💕💕作者: 爱笑学姐 💕💕个人简介:十年Java,Python美女程序员一枚,精通计算机专业前后端各类框架。 💕💕各类成品Java毕设 。javaweb,ssm&#xf…

宇树ROS1开源模型在ROS2中Gazebo中仿真

以GO1为例 1. CMakelists.txt更新语法 cmake_minimum_required(VERSION 3.8) project(go1_description) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic) endif() # find dependencies find…

Web网页制作之爱家居的设计(静态网页)

一、使用的是PyCharm来敲写的代码(布局) 二、主要的html代码的介绍 这段代码展示了如何使用HTML和CSS创建一个结构化的网页,包含导航栏、新闻内容、图片展示和页脚信息。通过引入外部CSS文件,可以进一步美化和布局这些元素。 HTM…

Linux云计算SRE-第二十周

完成ELK综合案例里面的实验,搭建完整的环境 一、 1、安装nginx和filebeat,配置node0(10.0.0.100),node1(10.0.0.110),node2(10.0.0.120),采用filebeat收集nignx日志。 #node0、node1、node2采用以下相同方式收集ngin…

【MATLAB例程】AOA(到达角度)法,多个目标定位算法,三维空间、锚点数量自适应(附完整代码)

给出AOA方法下的多目标定位,适用三维空间,锚点数量>3即可,可自定义目标和锚点的数量、坐标等。 文章目录 运行结果源代码代码讲解概述功能代码结构运行结果 10个锚点、4个目标的情况: 100个锚点、10个目标的情况: 修改方便,只需调节下面的两个数字即可: 源代码 …

Matlab:矩阵运算篇——矩阵数学运算

目录 1.矩阵的加法运算 实例——验证加法法则 实例——矩阵求和 实例——矩阵求差 2.矩阵的乘法运算 1.数乘运算 2.乘运算 3.点乘运算 实例——矩阵乘法运算 3.矩阵的除法运算 1.左除运算 实例——验证矩阵的除法 2.右除运算 实例——矩阵的除法 ヾ( ̄…

MinIO问题总结(持续更新)

目录 Q: 之前使用正常,突然使用空间为0B,上传文件也是0B(部署在k8s中)Q: 无法上传大文件参考yaml Q: 之前使用正常,突然使用空间为0B,上传文件也是0B(部署在k8s中) A: 1、检查pod状态…

智算新纪元,腾讯云HAI-CPU助力法律援助

高性能应用服务 1. ChatbotUI ​应用介绍 基于腾讯云 ​DeepSeek 模型的智能化对话界面,支持灵活集成到企业级应用或服务中,提供自然语言交互能力,适用于客服、知识检索、任务自动化等场景。 ​核心功能 ​多轮对话引擎:支持上下…

android 调用wps打开文档并感知保存事件

需求场景 在项目开发中会碰到需要调用WPS打开Word,Excel,Ppt等Office系列文档的情况,网上目前少有正式介绍如何调用相关API打开文档,并实现文档编辑后回传给三方应用,本人在逛WPS社区时发现 解锁WPS二次开发新世界:Android开发用…

【fnOS飞牛云NAS本地部署跨平台视频下载工具MediaGo与远程访问下载视频流程】

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

PyQt基础——简单的窗口化界面搭建以及槽函数跳转

一、代码实现 import sysfrom PyQt6.QtGui import QPixmap from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit, QMessageBox from PyQt6.uic import loadUi from PyQt6.QtCore import Qtclass LoginWindow(QWidget):def __init__(self):sup…

【Java--数据结构】优先级队列( PriorityQueue)

一. 优先级队列 1.1 优先级队列的概念 优先级队列是一种特殊的队列,它在入队时会根据元素的优先级进行排序,优先级最高的元素排在队列的前面,出队时会优先出队优先级最高的元素。 1.2 优先级队列的区别 (1)与普通…

【 <一> 炼丹初探:JavaWeb 的起源与基础】之 JavaWeb 项目的部署:从开发环境到生产环境

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、开发环境…

【AIGC】OpenAI 集成 Langchain 操作实战使用详解

目录 一、前言 二、前置准备 2.1 安装 Langchain必须的依赖 2.1.1 python环境 2.1.2 langchain openai 环境 2.1.3 准备一个apikey 2.1.4 langchain 核心组件 三、Langchain 各组件使用 3.1 Chat models组件 3.1.1 Invocation 使用 3.1.1.1 结果解析 3.2 提示词模板…

Xxl-Job学习笔记

目录 概述 核心架构 核心特点 应用场景 什么是任务调度 快速入门 获取源码 初始化调度数据库 基本配置 数据源datasource 邮箱email&#xff08;可选&#xff09; 会话令牌access token 启动调度中心 启动执行器 依赖 yaml基本配置 XxlJobConfig类配置 定义执…

SAIL-RK3576核心板应用方案——无人机视觉定位与地面无人设备通信控制方案

本方案以 EFISH-RK3576-SBC工控板 或 SAIL-RK3576核心板 为核心&#xff0c;结合高精度视觉定位、实时通信与智能控制技术&#xff0c;实现无人机与地面无人设备的协同作业。方案适用于物流巡检、农业植保、应急救援等场景&#xff0c;具备高精度定位、低延迟通信与强环境适应性…