Go 之 Gin 框架

Gin 是一个 Go (Golang) 编写的轻量级 web 框架,运行速度非常快,擅长 Api 接口的高并发,如果项目的规模不大,业务相对简单,这个时候我们也推荐您使用 Gin,特别适合微服务框架。

我自己也是Go开发方面的菜鸟,额外的就不多废话了。

简单路由配置

package main

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

func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// 配置路由
	r.GET("/", func(c *gin.Context) {
		aid := c.Query("aid")
		c.JSON(200, gin.H{
			"username": "name1",
			"aid": aid,
			"data": []string{"hello", "world"},
		})
	})
	// 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务
	r.Run()
}

运行起来以后,在浏览器输入http://127.0.0.1:8080/?aid=xyz,即可获取到 url 请求的结果

{"aid":"xyz","data":["hello","world"],"username":"name1"}

动态路由

所谓动态路由,其实就是将传参作为 url 的一部分,这样的话,url 就不再是固定不变的,而是随着传参的变化而变化,像 Ruby 等其他语言也有类似的用法。

package main

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

func main() {
	r := gin.Default()
	r.GET("/user/:id", func(c *gin.Context) {
		id := c.Param("id")
		c.JSON(200, gin.H{
			"username": "name1",
			"id": id,
			"data": []string{"hello", "world"},
		})
	})
	r.Run()
}

请求 url:http://127.0.0.1:8080/user/looking

请求 result:

{"data":["hello","world"],"id":"looking","username":"name1"}

结果响应

c.String()

package main

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

func main() {
	r := gin.Default()
	r.GET("/news", func(c *gin.Context) {
		c.String(200, "Hello world")
	})
	r.Run()
}

c.JSON()

大部分时候,我们直接返回 json 的数据格式要更多一些。数据返回我们可以使用 gin.H 的 map 形式,也可以直接用 struct 的形式,不过使用结构体的话,记得要给字段标注好 json 对应的 tag,方便直接将结构体实例解析成对应的 json 数据。

package main

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

func main() {
	r := gin.Default()
	r.GET("/structJson", func(c *gin.Context) {
		// 结构体方式
		var msg struct {
			Username string `json:"username"`
			Msg string `json:"msg"`
			Age string `json:"age"`
		}
		msg.Username = "name1"
		msg.Msg = "msg1"
		msg.Age = "18"
		c.JSON(200, msg)
	})
	r.Run()
}

c.JSONP()

这个暂时用的比较少

package main

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

func main() {
	r := gin.Default()
	r.GET("/JSONP", func(c *gin.Context) {
		data := map[string]interface{}{
			"foo": "bar",
		}
		c.JSONP(http.StatusOK, data)
	})
	r.Run()
}

请求 url:http://127.0.0.1:8080/JSONP?callback=x

请求 result:

x({"foo":"bar"});

c.HTML()

templates/index.html

<!DOCTYPE  html>
<html  lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<h1>这是一个 html 模板</h1>

<h3>{{.title}}</h3>
</body>
</html>

渲染之前,先对文件进行 load 加载,框架会自动将变量替换到 html 文件里进行渲染 

package main

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

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

	r.LoadHTMLFiles("./templates/index.html")
	r.GET("/index", func(c *gin.Context) {
		c.HTML(
			http.StatusOK, "index.html",
			map[string]interface{}{"title": "前台首页"})
	})

	r.Run()
}

http://127.0.0.1:8080/index

请求传值

get查询和动态路由前面已经有示例了,我们看下其他类型的传值。

post获取表单数据

package main

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

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

	r.POST("/doAddUser", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")
		age := c.DefaultPostForm("age", "20")
		c.JSON(200, gin.H{
			"usernmae": username, "password": password, "age": age,
		})
	})
	r.Run()
}

post/get传值绑定到结构体

传值绑定结构体估计是我们正常开发时最常用的参数解析方式之一了(至少我周围同事大部分都用这种形式传值)

package main

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

type Userinfo struct {
	Username string `form:"username" json:"user"`
	Password string `form:"password" json:"password"`
}

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

	r.GET("/", func(c *gin.Context) {
		var userinfo Userinfo
		if err := c.ShouldBind(&userinfo); err == nil {
			fmt.Printf("userinfo: %+v\n", userinfo) // userinfo: {Username:zhangsan Password:123456}
			c.JSON(http.StatusOK, userinfo)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	r.Run()
}

http://127.0.0.1:8080/?username=zhangsan&password=123456

{"user":"zhangsan","password":"123456"}

同理,POST请求等也可以将请求参数绑定到结构体中

package main

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

type Userinfo struct {
	Username string `form:"username" json:"user"`
	Password string `form:"password" json:"password"`
}

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

	r.POST("/doLogin", func(c *gin.Context) {
		var userinfo Userinfo
		if err := c.ShouldBind(&userinfo); err == nil {
			c.JSON(http.StatusOK, userinfo)
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		}
	})

	r.Run()
}

post获取xml数据

一般请求传递 xml 格式数据的遇到的不多,不过也可以试试。

package main

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

type Article struct {
	Title   string `json:"title" xml:"title"`
	Content string `json:"content" xml:"content"`
}

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

	r.POST("/xml", func(ctx *gin.Context) {
		var article Article
		if err := ctx.ShouldBindXML(&article); err == nil {
			fmt.Printf("article: %+v\n", article)
			ctx.JSON(http.StatusOK, article)
		}else {
			ctx.JSON(http.StatusBadRequest, gin.H {
				"err": err.Error()})
		}
	})

	r.Run()
}

可以使用 Apifox 发送请求尝试,可以直观看到接口返回的结果

路由分组

路由分组即将相关的路由加上相同的前缀,用以和其他路由进行区分和辨别(我自己理解是这样,分组依据一般可以按照业务等进行划分)。

package main

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

func ApiRouter(r *gin.Engine) {
	apiRouter := r.Group("/api")
	{
		apiRouter.GET("/", func(ctx *gin.Context) {
			ctx.String(200, "api")
		})

		apiRouter.GET("articles", func(ctx *gin.Context) {
			ctx.String(200, "/api/articles")
		})
	}
}

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

	r.Run()
}

路由分离

路由分离可以将不相关的路由解耦,分离到单独的文件进行维护。

在项目新建文件夹 router, 然后在router目录下创建apiRouter.go 和adminRouter.go

router/apiRouter.go

package router
// apiRouter.go
import "github.com/gin-gonic/gin"

func ApiRouter(r *gin.Engine) {
	apiRouter := r.Group("/api")
	{
		apiRouter.GET("/", func(ctx *gin.Context) {
			ctx.String(200, "api")
		})

		apiRouter.GET("articles", func(ctx *gin.Context) {
			ctx.String(200, "/api/articles")
		})
	}
}

router/apiAdmin.go 

package router

// adminRouter.go
import (
	"net/http"

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

func AdminRouter(r *gin.Engine) {
	adminRouter := r.Group("/admin")
	{
		adminRouter.GET("/", func(ctx *gin.Context) {
			ctx.String(200, "admin")
		})

		adminRouter.GET("list", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "admin/list")
		})
	}
}

然后在 main.go 中引入路由模块并使用即可(在真实的开发中,main.go 中的内容其实很少,一般只是一个启动整个服务的入口)

package main

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

	"github.com/test/router"
)


func main() {
	// 创建一个默认的路由引擎
	r := gin.Default()
	// 引入路由模块
	router.AdminRouter(r)
	router.ApiRouter(r)
	// 启动 HTTP 服务,默认在 0.0.0.0:8080 启动服务
	r.Run()
}

自定义控制器

当我们的项目比较大的时候有必要对我们的控制器进行分组 , 业务逻辑放在控制器中(有的喜欢把业务逻辑处理部分所在的包称为 handler)。

新建 controller/api/userController.go

package api

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

func UserIndex(c *gin.Context) {
	c.String(200, "api UserIndex")
}

func UserAdd(c *gin.Context)  {
	c.String(200, "api UserAdd")
}

func UserList(c *gin.Context) {
	c.String(200, "api UserList")
}
func UserUpdate(c *gin.Context) {
	c.String(200, "api UserUpdate")
}

func UserDelete(c *gin.Context) {
	c.String(200, "api UserDelete")
}

router/apiRouter.go

package router
// apiRouter.go
import (
	"github.com/gin-gonic/gin"
	"github.com/test/controller/api"
)

func ApiRouter(r *gin.Engine) {
	apiRouter := r.Group("/api")
	{
		apiRouter.GET("/")

		apiRouter.GET("/users", api.UserIndex)
		apiRouter.GET("/users/:id", api.UserList)
		apiRouter.POST("/users", api.UserAdd)
		apiRouter.PUT("/users/:id", api.UserUpdate)
		apiRouter.DELETE("/users", api.UserDelete)

	}
}

控制器继承

要让控制器可以继承,最好将控制器做成方法的形式(一般默认是函数的形式),这样的话,就可以根据结构体的匿名字段,实现对继承结构体的方法进行很方便的调用。

controller/api/baseController.go

package api

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

type BaseController struct {
}

func (con BaseController) Success(c *gin.Context) {
	c.String(200, "success")
}

func (con BaseController) Error(c *gin.Context) {
	c.String(200, "failed")
}

controller/api/userController.go

package api

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

type UserController struct {
	BaseController
}

func (con UserController) UserIndex(c *gin.Context) {
	// c.String(200, "api UserIndex")
	con.Success(c)
}

func (con UserController) UserAdd(c *gin.Context) {
	c.String(200, "api UserAdd")
}

func (con UserController) UserList(c *gin.Context) {
	c.String(200, "api UserList")
}
func (con UserController) UserUpdate(c *gin.Context) {
	c.String(200, "api UserUpdate")
}

func (con UserController) UserDelete(c *gin.Context) {
	c.String(200, "api UserDelete")
}

apiRouter.go

package router
// apiRouter.go
import (
	"github.com/gin-gonic/gin"
	"github.com/test/controller/api"
)

func ApiRouter(r *gin.Engine) {
	apiRouter := r.Group("/api")
	{
		apiRouter.GET("/")

		apiRouter.GET("/index", api.UserController{}.UserIndex)
		apiRouter.GET("/users", api.UserController{}.UserList)
		apiRouter.GET("/users/:id", api.UserController{}.UserList)
		apiRouter.POST("/users", api.UserController{}.UserAdd)
		apiRouter.PUT("/users/:id", api.UserController{}.UserUpdate)
		apiRouter.DELETE("/users", api.UserController{}.UserDelete)

	}
}

To be continued 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot的“校园台球厅人员与设备管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“校园台球厅人员与设备管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 系统首页界面图…

XUbuntu22.04之激活Linux最新Typora版本(二百二十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Python数据结构实验 图实验(二)

一、实验目的 1&#xff0e;掌握生成树和最小生成树方法&#xff0c;包括普里姆算法设计和克鲁斯卡尔算法设计&#xff1b; 2&#xff0e;掌握求解图的最短路径方法&#xff0c;包括单源最短路径的狄克斯特拉算法设计和多源最短路径的弗洛伊德算法设计&#xff1b; 3&#x…

动态规划——回文串问题

目录 练习1&#xff1a;回文子串 练习2&#xff1a;最长回文子串 练习3&#xff1a;回文串分割IV 练习4&#xff1a;分割回文串 练习5&#xff1a;最长回文子序列 练习6&#xff1a;让字符串成为回文串的最小插入次数 本篇文章主要学习使用动态规划来解决回文串相关问题&…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑新能源发电商租赁共享储能的电力市场博弈分析》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

将使用realsense相机录制的bag转化为TUM数据集格式

GitHub - kinglintianxia/bag2tum: ROS bag to tum dataset style files 基于以上代码进行实现&#xff1a; 1.创建文件夹&#xff1a; image ├── depth └── rgb 2.修改bag2tum.launch文件中的&#xff1a;save_folder, rgb_topic 和depth_topic参数&#xff1a; <par…

LeetCode Python - 83. 删除排序链表中的重复元素

目录 题目描述解法运行结果 题目描述 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 示例 2&#xff1a; 输入&#x…

LeetCode题练习与总结:N皇后

一、题目描述 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决…

Matlab将日尺度数据转化为月尺度数据

日尺度转化为月尺度 clcclear all% load datadata xlread(data.xlsx) % 例如该数据为1961-01-01至2022-12-31&#xff0c;共计22645天data data(:,1:3) % 该数据有22645行&#xff0c;数据分别为降水&#xff0c;气温&#xff0c;湿度等三列dt datetime(1961-01-01):datatim…

政安晨:【Keras机器学习实践要点】(十)—— 自定义保存和序列化

目录 导言 涵盖的API Setup 状态保存自定义 构建和编译保存自定义 结论 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在…

线程的安全问题

目录 导言&#xff1a; 正文&#xff1a; 1.共享资源&#xff1a; 2.非原子操作&#xff1a; 3.执行顺序不确定&#xff1a; 4.可见性&#xff1a; 5.死锁和饥饿&#xff1a; 6.指令重排序&#xff1a; 总结&#xff1a; 导言&#xff1a; 线程安全是并发编程中的一个…

文献阅读:使用 CellChat 推理和分析细胞-细胞通信

文献介绍 「文献题目」 Inference and analysis of cell-cell communication using CellChat 「研究团队」 聂青&#xff08;加利福尼亚大学欧文分校&#xff09; 「发表时间」 2021-02-17 「发表期刊」 Nature Communications 「影响因子」 16.6 「DOI」 10.1038/s41467-0…

Vue3 使用 v-bind 动态绑定 CSS 样式

在 Vue3 中&#xff0c;可以通过 v-bind 动态绑定 CSS 样式。 语法格式&#xff1a; color: v-bind(数据); 基础使用&#xff1a; <template><h3 class"title">我是父组件</h3><button click"state !state">按钮</button>…

解析CUDA FATBIN格式

参考文档&#xff1a; https://pdfs.semanticscholar.org/5096/25785304410039297b741ad2007e7ce0636b.pdf CUDA Pro Tip: Understand Fat Binaries and JIT Caching | NVIDIA Technical Blog cuda二进制文件中到底有什么 - 知乎 NVIDIA CUDA Compiler Driver NVIDIA CUDA…

HSP_04章_扩展: 进制、位运算

文章目录 10. 扩展: 进制11. 位运算11.1 二进制在运算中的说明11.2 原码 反码 补码11.3位运算符11.3.1 ~按位取反11.3.2 &按位与11.3.3 ^按位异或11.3.4 |按位或11.3.5 << 左移11.3.6 >> 右移 10. 扩展: 进制 进制介绍 进制的转换 2.1 其他进制转十进制 二进…

(八)目标跟踪中参数估计(似然、贝叶斯估计)理论知识

目录 前言 一、统计学基础知识 &#xff08;一&#xff09;随机变量 &#xff08;二&#xff09;全概率公式 &#xff08;三&#xff09;高斯分布及其性质 二、似然是什么&#xff1f; &#xff08;一&#xff09;概率和似然 &#xff08;二&#xff09;极大似然估计 …

国内顶级大牛整理:分布式消息中间件实践笔记+分布式核心原理解析

XMPP JMS RabbitMQ 简介 工程实例 Java 访问RabbitMQ实例 Spring 整合RabbitMQ 基于RabbitMQ的异步处理 基于RabbitMQ的消息推送 RabbitMQ实践建议 虚拟主机 消息保存 消息确认模式 消费者应答 流控机制 通道 总结 ActiveMQ 简介 工程实例 Java 访问ActiveMQ实例…

机器人寻路算法双向A*(Bidirectional A*)算法的实现C++、Python、Matlab语言

机器人寻路算法双向A*&#xff08;Bidirectional A*&#xff09;算法的实现C、Python、Matlab语言 最近好久没更新&#xff0c;在搞华为的软件挑战赛&#xff08;软挑&#xff09;&#xff0c;好卷只能说。去年还能混进32强&#xff0c;今年就比较迷糊了&#xff0c;这东西对我…

EasyRecovery2024汉化精简版,无需注册

EasyRecovery2024是世界著名数据恢复公司 Ontrack 的技术杰作&#xff0c;它是一个威力非常强大的硬盘数据恢复软件。能够帮你恢复丢失的数据以及重建文件系统。 EasyRecovery不会向你的原始驱动器写入任何东东&#xff0c;它主要是在内存中重建文件分区表使数据能够安全地传输…

FL Studio21.2.3中文版软件新功能介绍及下载安装步骤教程

FL Studio21.2中文版的适用人群非常广泛&#xff0c;主要包括以下几类&#xff1a; FL Studio 21 Win-安装包下载如下: https://wm.makeding.com/iclk/?zoneid55981 FL Studio 21 Mac-安装包下载如下: https://wm.makeding.com/iclk/?zoneid55982 音乐制作人&#xff1a…