Go开发指南-Gin与Web开发

目录:
(1)Go开发指南-Hello World
(2)Go开发指南-Gin与Web开发

Gin 是一个用 Go 语言编写的轻量级、高性能的 Web 框架,主要用于构建 API 服务和微服务。由于其简洁的 API 设计和强大的路由功能,Gin 在 Go 社区中广受欢迎。

运行Web程序

创建一个目录web-service-gin,初始化模块

go mod init example/web-service-gin

创建main.go

package mainimport ("net/http""github.com/gin-gonic/gin"
)type album struct {ID     string  `json:"id"`Title  string  `json:"title"`Artist string  `json:"artist"`Price  float64 `json:"price"`
}func main() {router := gin.Default()router.GET("/albums", getAlbums)router.Run("localhost:8080")
}var albums = []album{{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}func getAlbums(c *gin.Context) {c.IndentedJSON(http.StatusOK, albums)
}

将其运行起来: go run .,就能访问http://lcoalhost:8080/albums了。

再新增一个POST接口:


// add post handler
func postAlbums(c *gin.Context) {var newAlbum albumif err := c.BindJSON(&newAlbum); err != nil {return}albums = append(albums, newAlbum)c.IndentedJSON(http.StatusCreated, newAlbum)
}// update router
router.POST("/albums", postAlbums)

此时用POST方法来访问就可以来新增album了。

根据指定id返回

新增一个接口:

// add getById handler
func getAlbumByID(c *gin.Context) {id := c.Param("id")for _, a := range albums {if a.ID == id {c.IndentedJSON(http.StatusOK, a)return}}c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}// update router
router.GET("/albums/:id", getAlbumByID)

发起请求:curl http://localhost:8080/albums/1 即可获取第一个album了。

连接数据库

Web服务中访问数据库是必不可少的,下面以postgresql数据库为例来演示如何连接数据库。

先创建数据库和表:

CREATE DATABASE music;CREATE TABLE album(id SERIAL PRIMARY KEY,title VARCHAR(36) NOT NULL,artist VARCHAR(36) NOT NULL,price NUMBRIC(10, 2) NOT NULL
) ;INSERT INTO album(title, artist, price) VALUES
('Album A', 'Artist 1', 9.99),
('Album B', 'Artist 1', 14.99),
('Album C', 'Artist 2', 19.99);

创建main.go:

package mainimport ("database/sql""fmt""log""net/http""github.com/gin-gonic/gin"_ "github.com/lib/pq"
)type Album struct {ID     string  `json:"id"`Title  string  `json:"title"`Artist string  `json:"artist"`Price  float64 `json:"price"`
}var db *sql.DBfunc main() {dsn := "user=postgres password=admin dbname=music sslmode=disable"var err errordb, err = sql.Open("postgres", dsn)if err != nil {log.Fatal("Failed to connect to database", err)}pingErr := db.Ping()if pingErr != nil {log.Fatal("Failed to ping database", pingErr)}fmt.Println("Connected!")router := gin.Default()router.GET("/albums/:artist", getAlbumsByArtist)router.Run("localhost:8080")
}func getAlbumsByArtist(c *gin.Context) {artist := c.Param("artist")albums, err := queryAlbumsByArtist(artist)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})return}if len(albums) == 0 {c.JSON(http.StatusNotFound, gin.H{"message": "No albums found for artist"})return}c.JSON(http.StatusOK, albums)
}func queryAlbumsByArtist(artist string) ([]Album, error) {rows, err := db.Query("SELECT * FROM album WHERE artist = $1", artist)if err != nil {return nil, err}defer rows.Close()var albums []Albumfor rows.Next() {var album Albumif err := rows.Scan(&album.ID, &album.Title, &album.Artist, &album.Price); err != nil {return nil, err}albums = append(albums, album)}if err = rows.Err(); err != nil {return nil, err}return albums, nil
}

注意,在导入包时使用了 _ "github.com/lib/pq", 此处_表示该包被导入,但不直接在代码中使用,其作用是执行该包的初始化函数。这种用法称为空白标识符导入,通常用于以下几种情况:

  • 注册驱动程序或插件
  • 执行包的初始化代码

访问curl http://localhost:8080/albums/Artist%201,即可获取返回结果。

Gin的中间件

Gin的中间件函数可以在请求到达handler之前做一些前置处理或者在响应返回给客户端之前做后置处理。Gin提供了很多内置的中间件函数,比如常见的日志记录,CORS处理等。开发者也可以根据需要自己定制中间件函数。

日志中间件

下面以内置的日志中间件函数为例来说明如何做日志前处理:

package mainimport ("log""time""github.com/gin-gonic/gin"
)func LoggerMiddleware() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()c.Next()duration := time.Since(start)log.Printf("Request - Method: %s | Status : %d | Duration: %v", c.Request.Method, c.Writer.Status(), duration)}
}func main() {router := gin.Default()router.Use(LoggerMiddleware())router.GET("/", func(c *gin.Context) {c.String(200, "Hello, World!")})router.Run(":8080")
}

访问 http://localhost:8080/, 会看到输出日志中打印日志:

Request - Method: GET | Status : 200 | Duration: 24.291µs

自定义中间件

假设我们需要在请求被处理之前对其进行鉴权,可以自定义中间件:

package mainimport ("github.com/gin-gonic/gin"
)func AuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {apiKey := c.GetHeader("X-API-Key")if apiKey == "" {c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})return}c.Next()}
}func main() {router := gin.Default()authGroup := router.Group("/api")authGroup.Use(AuthMiddleware()){authGroup.GET("/data", func(c *gin.Context) {c.JSON(200, gin.H{"message": "Authenticated and authorized!"})})}router.Run(":8080")
}

当访问http://localhost:8080/api/data时,返回{"error":"Unauthorized"}错误。

路由分组

上文中设置鉴权中间件时对路由进行了分组。Gin允许对路由进行分组,以便更好地组织和维护代码。

下面继续展示路由分组功能:

package mainimport ("github.com/gin-gonic/gin"
)func AuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {apiKey := c.GetHeader("X-API-Key")if apiKey == "" {c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})return}c.Next()}
}func main() {router := gin.Default()public := router.Group("/public"){public.GET("/info", func(c *gin.Context) {c.String(200, "Public information")})public.GET("/products", func(c *gin.Context) {c.String(200, "Public product list")})}private := router.Group("/private")private.Use(AuthMiddleware()){private.GET("/data", func(c *gin.Context) {c.String(200, "Private data accessible after authentication")})private.POST("/create", func(c *gin.Context) {c.String(200, "Create a new resource")})}router.Run(":8080")
}

控制器与Handlers

当后端接口不断增加时,如果将所有的业务逻辑全部放在路由的handlers里面是不明智的。

最好是增加控制器来处理业务逻辑,如下所示:

package mainimport ("github.com/gin-gonic/gin"
)type UserController struct{}func (uc *UserController) GetUserInfo(c *gin.Context) {userID := c.Param("id")c.JSON(200, gin.H{"id": userID, "name": "John Doe", "email": "john@example.com"})
}func main() {router := gin.Default()userController := &UserController{}router.GET("/users/:id", userController.GetUserInfo)router.Run(":8080")
}

参考资料

[1]. https://go.dev/doc/tutorial/web-service-gin

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

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

相关文章

JMeter进阶篇

目录 上篇导航: 总目录: 一、逻辑控制器: 1.逻辑控制器和关联: 2.if逻辑控制器: 3.forEach控制器: 4.循环控制器: 二、关联: 1.xpath: 2.正则表达式提取器&…

homework 2024.11.10 math 1 - math 6

小学1年级和小学6年级数学图形结合,以及习惯养成,过程改进 6年级数学: 一年级数学:

【Linux 31】网络层协议 - IP

文章目录 🌈 一、IP 协议的基本概念⭐ 1. TCP IP 能保证数据的可靠传输⭐ 2. 如何理解 IP地址⭐ 3. 路由选择⭐ 4. 主机 & 路由器 & 节点 🌈 二、IP 协议的报头格式⭐ 1. IP 协议的报头格式⭐ 2. 如何将 IP 报头和有效载荷分离⭐ 3. 如何将有效…

Mac中安装OhMyZsh

Mac中安装OhMyZsh 文章目录 Mac中安装OhMyZsh一、Homebrew二、OhMyZsh1、Oh-My-Zsh配置1.1:主题配置1.2:插件配置(语法高亮和自动提示)1、zsh-autosuggestions(需下载安装):高亮显示所有支持的命…

7、computed计算属性使用

代码 Student.vue <template> <div><h2>computed计算属性使用</h2><input type"text" v-model"name"/><br/><input type"text" v-model"sex"/><br/>完整信息&#xff1a;{{info}}&…

SystemVerilog学习笔记(三):结构体与联合体

结构体 结构包含具有不同大小的不同数据类型&#xff0c;这些数据类型分组在一个结构体名称下。默认情况下&#xff0c;结构体最初是未压缩的形式&#xff0c;但可以使用“packed”关键字将其转换为压缩结构。 结构与普通数组不同&#xff0c;因为数组仅使用相同类型和大小的…

无人飞手培训机构大量新增,如何选择好的培训机构?

随着无人机技术的普及和应用领域的拓展&#xff0c;无人机飞手培训机构确实在大量新增。为了选择一家好的培训机构&#xff0c;可以从以下几个方面进行考量&#xff1a; 一、培训资质 官方认证&#xff1a;选择具备中国航空器拥有者及驾驶员协会&#xff08;AOPA-China&#x…

Wi-Fi背后的工作原理与技术发展历程介绍【无线通信小百科】

1个视频说清楚WIFI&#xff1a;频段/历程/技术参数/常用模块 智能手机拥有率越来越高的今天&#xff0c;大家已经习惯了通过无线网络上网的方式。除了在外面需要用手机流量&#xff0c;我们通常在家里或者机场&#xff0c;商场都可以通过Wi-Fi连接上网。本期文章将为大家介绍Wi…

入门车载以太网(4) -- 传输层(TCP\UDP)

目录 1.ECU通信方式的变化 2.传输层概述 2.1 UDP 2.2 TCP 3. TCP和ISO 15765-2 1.ECU通信方式的变化 我们先回顾下两种通信方式&#xff1a;Signal-Based Messaging、Service-Based Messaging。 Signal-Based Messaging 基于信号的通信方式&#xff0c;例如CAN通信&…

软件测试第二篇软件测试技术

第五章单元测试和集成测试的技术 单元静态测试主要由开发人员完成。 标准&#xff1a;规定什么能做&#xff0c;什么不能做。 规范&#xff1a;建议你要怎么做。 5.1.2 代码评审 代码评审是一种发现代码缺陷的另一种测试方法。 代码审查的最佳实践&#xff1a; 创建代码审…

w035基于web的学科竞赛管理

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…

导航系统非完整性约束汽车运动公约

Back to FDISYSTEMS Knowledge Base 惯性&导航传感器 导航系统&运动约束 数学基础 & 约定 参考坐标系载体 & 传感器坐标系方向/旋转表示非线性卡尔曼滤波器SPKF汽车运动公约船舶运动公约 惯性传感器安装 惯性传感器运行 技术洞察 Knowledge Base /数学基础…

Centos8 安装 JDK / Python / MySQL / Redis / Nginx

安装 JDK 华为镜像 JDK 下载地址&#xff1a;https://repo.huaweicloud.com:8443/artifactory/java-local/jdk/ 这里安装 JDK8 为例&#xff1a; # 这里直接通过 wget 下载 wget https://repo.huaweicloud.com:8443/artifactory/java-local/jdk/8u202-b08/jdk-8u202-linux-x…

【Qt-ROS开发】使用 Qt Creator 构建和编译含 ROS 库的 Qt 项目

【Qt-ROS】使用 Qt Creator 构建和编译含 ROS 库的项目 网上大多数办法是在 Qt creator中安装 ros_qtc_plugin 插件&#xff0c;项目以 ROS1 工作空间的形式构建&#xff0c;还是使用 catkin 来构建整个项目。但是这种方式局限很大&#xff0c;导入 Qt 的组件反而变得很麻烦&a…

【RabbitMQ】07-业务幂等处理

1. 方式一 序列化设置唯一Id。 Beanpublic MessageConverter messageConverter() {Jackson2JsonMessageConverter jjmc new Jackson2JsonMessageConverter();jjmc.setCreateMessageIds(true);return jjmc;}RabbitListener(bindings QueueBinding(value Queue(name "d…

SparseDrive 论文学习

论文链接&#xff1a;https://arxiv.org/pdf/2405.19620 代码链接&#xff1a;https://github.com/swc-17/SparseDrive 解决了什么问题&#xff1f; 传统模块化的自动驾驶系统可以被解耦为不同的独立模块&#xff0c;如感知、预测和规划&#xff0c;这种范式会面临信息丢失和…

如何提高自动驾驶中惯性和卫星组合导航pbox的精度?

Mems纯惯导里程推算精度做到千分之一&#xff0c;两分钟航向精度保持0.001弧度&#xff0c;是如何做到的&#xff1f; 简单的来说&#xff0c;导航系统的误差来源于这三方面:1.传感器误差 2.时间和迭代频率 3.算法精度。 接下来逐一分析。 1.传感器误差&#xff0c;传感器误差…

机器学习——贝叶斯

&#x1f33a;历史文章列表&#x1f33a; 机器学习——损失函数、代价函数、KL散度机器学习——特征工程、正则化、强化学习机器学习——常见算法汇总机器学习——感知机、MLP、SVM机器学习——KNN机器学习——贝叶斯机器学习——决策树机器学习——随机森林、Bagging、Boostin…

20241111,LeetCode 每日一题,用 Go 实现旋转链表

题目 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 解题思路 计算链表长度&#xff1a;遍历链表来获取链表的长度 n&#xff0c;因为链表的旋转其实是循环移动&#xff0c;所以将 k 对 n 取模 k k % n&#xff0c;这样可以…

Linux驱动开发(4):Linux的设备模型

在前面写的驱动中&#xff0c;我们发现编写驱动有个固定的模式只有往里面套代码就可以了&#xff0c;它们之间的大致流程可以总结如下&#xff1a; 实现入口函数xxx_init()和卸载函数xxx_exit() 申请设备号 register_chrdev_region() 初始化字符设备&#xff0c;cdev_init函数…