百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Go语言gin框架从入门到精通(go语言orm框架)

ccwgpt 2024-09-18 12:19 24 浏览 0 评论

1. 简介

1.1. 介绍

  • Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
  • 对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错
  • 借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范

1.2. 安装

使用Mod依赖管理工具,在项目根目录执行 :

go mod init

然后项目根目录下会生成 go.mod文件,修改文件添加gin相关依赖

require github.com/gin-gonic/gin v1.6.3

运行项目时会自动下载相关依赖。

1.3. 第一个程序Hello World!

package main

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

func main() {
    //1.创建路由
    r := gin.Default()
    //2.绑定路由规则,执行的函数
    r.GET("/", func(context *gin.Context) {
        context.String(http.StatusOK, "Hello World!")
    })
    //3.监听端口,默认8080
    r.Run(":8080")
}

编译运行,项目会自动下载gin依赖。在浏览器输入 http://127.0.0.1:8080,出现以下页面说明运行成功:

2 Gin的路由

2.1基本路由

  • gin 框架中采用的路由库是基于httprouter做的
  • 地址为:https://github.com/julienschmidt/httprouter
func main() {

    r := gin.Default()
    r.GET("/", func(context *gin.Context) {
        context.String(http.StatusOK, "Hello")
    })
    r.POST("/xxxpost",func())
    r.PUT("/xxxput",func())

    r.Run(":8080")
}

2.1 Restful风格的API

  • gin支持Restful风格的API
  • 即Representational State Transfer的缩写。直接翻译的意思是"表现层状态转化",是一种互联网应用程序的API设计理念:URL定位资源,用HTTP描述操作。如:

1.获取文章 /blog/getXxx Get blog/Xxx

2.添加 /blog/addXxx POST blog/Xxx

3.修改 /blog/updateXxx PUT blog/Xxx

4.删除 /blog/delXxxx DELETE blog/Xxx

2.2 API参数

  • 可以通过Context的Param方法获取API参数
  • localhost:8080/xxx/you
func main() {
    r := gin.Default()
    r.GET("user/:name/*action", func(context *gin.Context) {
        name := context.Param("name")
        action := context.Param("action")

        fmt.Println(action)
        //  截取/
        action = strings.Trim(action, "/")
        context.String(http.StatusOK, name+" is "+action)
    })

    r.Run(":8080")
}

2.3 URL参数

  • URL参数可以通过DefaultQuery()或Query()方法获取
  • DefaultQuery()若参数不村则,返回默认值,Query()若不存在,返回空串

go func main() { r := gin.Default() r.GET("user", func(context *gin.Context) { //指定默认值 //http://127.0.0.1/user name := context.DefaultQuery("name", "you") context.String(http.StatusOK, fmt.Sprintf("hello %s", name)) }) //默认为:8080 r.Run() }

编译,请求输出如下:

不带参数,显示默认参数:

带参数 name=thinks:

2.4 表单参数

  • 表单传输为post请求,http常见的传输格式为四种:
  • application/json
  • application/x-www-form-urlencoded
  • application/xml
  • multipart/form-data
  • 表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数

Html代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:8080/form" method="post">
    用户名:<input type="text" name="username" placeholder="请输入你的用户名"> <br>
    密   码:<input type="password" name="password" placeholder="请输入你的密码"> <br>
    <input type="submit" value="提交">
</form>
</body>
</html>

Go语言代码如下:

func main() {

    r := gin.Default()
    r.POST("/form", func(context *gin.Context) {
        types := context.DefaultPostForm("type", "post")
        username := context.PostForm("username")
        password := context.PostForm("password")

        context.String(http.StatusOK, fmt.Sprintf("username:%s , password:%s , types:%s", username, password, types))
    })

    r.Run()
}

编译请求输出:

2.5 单文件上传

  • multipart/form-data格式用于文件上传
  • gin文件上传与原生的net/http方法类似,不同在于gin把原生的request封装到c.Request中

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单文件上传</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
    上传文件:<input type="file" name="file">
    <input type="submit" value="提交">
</form>

</body>
</html>
func main() {

    r := gin.Default()
    //限制上传文件大小  8M
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(context *gin.Context) {
        file, err := context.FormFile("file")
        if err != nil {
            context.String(500, "上传图片出错")
        }
        // c.JSON(200, gin.H{"message": file.Header.Context})
        context.SaveUploadedFile(file, file.Filename)
        context.String(http.StatusOK, file.Filename)
    })

    r.Run()
}

运行效果如下:

2.6 多文件上传

HTML页面加入multiple关键字

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多文件上传</title>
</head>
<body>
<form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
    上传文件:<input type="file" name="files" multiple>
    <input type="submit" value="提交">
</form>

</body>
</html>

go语言代码

func main() {
    // 创建路由
    r := gin.Default()
    // 限制表单上传大小8MB,默认为32MB
    r.MaxMultipartMemory = 8 << 20
    r.POST("/upload", func(context *gin.Context) {
        form, err := context.MultipartForm()
        if err != nil {
            context.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
        }
        //获取所有文件
        files := form.File["files"]
        //遍历所有文件
        for _, file := range files {
            if err := context.SaveUploadedFile(file, file.Filename); err != nil {
                context.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
                return
            }
        }

        context.String(200, fmt.Sprintf("upload ok %d files", len(files)))
    })

    r.Run(":8080")
}

效果展示:

2.7 routes group

  • routes group是为了管理一些相同的URL

举个例子:

func main() {

    //创建路由
    r := gin.Default()
    //路由组1,处理get请求
    v1 := r.Group("/v1")
    {
        v1.GET("/login", login)
        v1.GET("/submit", submit)
    }
    v2 := r.Group("/v2")
    {
        v2.POST("/login", login)
        v2.POST("/submit", submit)
    }
    r.Run(":8080")
}

func login(c *gin.Context) {
    name := c.DefaultQuery("name", "jack")
    c.String(200, fmt.Sprintf("hello %s\n", name))
}

func submit(c *gin.Context) {
    name := c.DefaultQuery("name", "lily")
    c.String(200, fmt.Sprintf("hello %s\n", name))
}

在浏览器中分别输入:

http://127.0.0.1:8080/v1/login?name=you

http://127.0.0.1:8080/v1/submit?name=you

输出 hello you

使用windows自带的curl工具进行post请求,在cmd中输入

curl http://127.0.0.1:8080/v2/login -X POST

输出 hello jack

2.8 路由拆分与注册

2.8.1 基本的路由注册

下面最基础的gin路由注册方式,适用于路由条目比较少的简单项目或者项目demo。以上列子都是基本路由注册

func helloHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Hello www.topgoer.com!",
    })
}

func main() {
    r := gin.Default()
    r.GET("/topgoer", helloHandler)
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%v\n", err)
    }
}

2.8.2 路由拆分成单独的文件或包

当项目的规模增大后就不太适合继续在项目的main.go文件中去实现路由注册相关逻辑了,我们会倾向于把路由部分的代码都拆分出来,形成一个单独的文件或包。

我我们在routers.go文件中定义并注册路由信息:

package main

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

func helloHandler(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "message": "Hello Gin",
    })
}

func setupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/user", helloHandler)
    return r
}

此时main.go中调用上面定义好的setupRouter函数:

func main() {
    r := setupRouter()
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%v\n", err)
    }
}

此时的目录结构:

LearnGin
├── go.mod
├── go.sum
├── main.go
└── routers.go

把路由部分的代码单独拆分成包的话也是可以的,拆分后的目录结构如下:

LearnGin
├── go.mod
├── go.sum
├── main.go
└── routers
    └── routers.go

routers/routers.go 需要注意此时setupRouter需要改成首字母大写:

// SetupRouter 配置路由信息
func SetupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/topgoer", helloHandler)
    return r
}

2.8.3 路由拆分成多个文件

当我们的业务规模继续膨胀,单独的一个routers文件或包已经满足不了我们的需求了,

func SetupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/user", helloHandler)
    r.GET("/login", xxHandler1)
    ...
    r.GET("/register", xxHandler30)
    return r
}

因为我们把所有路由注册都写在一个SetupRouter函数中的话就会太复杂了。

我们可以分开定义多个路由文件,例如

LearnGin
├── go.mod
├── go.sum
├── main.go
└── routers
    ├── blog.go
    └── shop.go

routers/shop.go中添加一个LoadShop的函数,将shop相关的路由注册到指定的路由器

package routers

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

func LoadShop(e *gin.Engine)  {
    e.GET("/hello", helloHandler)
    e.GET("/goods", goodsHandler)
    e.GET("/checkout", checkoutHandler)
}
func helloHandler(c *gin.Context)  {}
func goodsHandler(c*gin.Context)  {}
func checkoutHandler(c*gin.Context)  {}

routers/blog.go中添加一个LoadBlog的函数,将blog相关的路由注册到指定的路由器

package routers

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

func LoadBlog(e *gin.Engine) {
    e.GET("/post", postHandler)
    e.GET("/comment", commentHandler)
}
func postHandler(c *gin.Context) {}
func commentHandler(c *gin.Context) {}

在main函数中实现最终的注册逻辑如下:

func main() {
    r := gin.Default()
    routers.LoadBlog(r)
    routers.LoadShop(r)
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%v\n", err)
    }
}

2.8.4 路由拆分到不同的APP

有时候项目规模实在太大,那么我们就更倾向于把业务拆分的更详细一些,例如把不同的业务代码拆分成不同的APP。

因此我们在项目目录下单独定义一个app目录,用来存放我们不同业务线的代码文件,这样就很容易进行横向扩展。大致目录结构如下:

LearnGin
├── app
│   ├── blog
│   │   ├── handler.go
│   │   └── router.go
│   └── shop
│       ├── handler.go
│       └── router.go
├── go.mod
├── go.sum
├── main.go
└── routers
    └── routers.go

其中app/blog/router.go用来定义post相关路由信息,具体内容如下:

func Routers(e *gin.Engine) {
    e.GET("/post", postHandler)
    e.GET("/comment", commentHandler)
}

app/shop/router.go用来定义shop相关路由信息,具体内容如下:

func Routers(e *gin.Engine) {
    e.GET("/goods", goodsHandler)
    e.GET("/checkout", checkoutHandler)
}

routers/routers.go中根据需要定义Include函数用来注册子app中定义的路由,Init函数用来进行路由的初始化操作:

type Option func(*gin.Engine)

var options = []Option{}

// 注册app的路由配置
func Include(opts ...Option) {
    options = append(options, opts...)
}

// 初始化
func Init() *gin.Engine {
    r := gin.New()
    for _, opt := range options {
        opt(r)
    }
    return r
}

main.go中按如下方式先注册子app中的路由,然后再进行路由的初始化:

func main() {
    // 加载多个APP的路由配置
    routers.Include(shop.Routers, blog.Routers)
    // 初始化路由
    r := routers.Init()
    if err := r.Run(); err != nil {
        fmt.Println("startup service failed, err:%v\n", err)
    }
}

相关推荐

5 分钟搭建 Node.js 微服务原型(node 微服务架构)

微服务已成为在Node.js中构建可扩展且强大的云应用的主流方法。同时也存在一些门槛,其中一些难点需要你在以下方面做出决策:组织项目结构。将自定义服务连接到第三方服务(数据库,消息代理等)处理微服...

当前的前端,真的不配叫程序员吗?

今天看到一个比较令人震惊的帖子,说前端不配叫程序员,令我很吃鲸,是谁我就不说了,帖子出处是一个大龄程序员组里面的,想想也不觉得奇怪了,毕竟对于年龄比较大的程序员来说,前端起步比较晚,最开始就是一个切图...

聊聊asp.net中Web Api的使用(asp.net core web api教程)

扯淡随着app应用的崛起,后端服务开发的也越来越多,除了很多优秀的nodejs框架之外,微软当然也会在这个方面提供更便捷的开发方式。这是微软一贯的作风,如果从开发的便捷性来说的话微软是当之无愧的老大哥...

NodeJS中,listen Access:permission denied解决办法

错误描述:Win10系统,NodeJS程序。使用express框架开发的http服务器,启动时出现错误提示“listenAccess:permissiondenied"。错误原因:这是由于...

Hono — 下一代高性能web框架(天融信下一代vnp)

最近公司可能要有变革,要统计我们的技能。真的是很无语,但是有没有办法。哎,问豆包吧提起Hono大家可能很陌生,这是什么?但是我提到Expressjs、nodejs想必前端小伙伴很熟悉啊。那么Hon...

生活例子说明线程,简单明了(列举一个日常生活中的例子以程序的形式表示)

1.程序设计的目标在我看来单从程序的角度来看,一个好的程序的目标应该是性能与用户体验的平衡。当然一个程序是否能够满足用户的需求暂且不谈,这是业务层面的问题,我们仅仅讨论程序本身。围绕两点来展开,性能...

Node实战006:自定义模块的创建和使用详解

Node的应用是由模块组成的,每个文件的定义都是一个模块(module变量代表当前模块)并有自己的作用域。Node遵循commonjs的模块规范,用来隔离每个模块的作用域,使每一个模块在自身的命名空间...

Node.js基本内容和知识点(node.js的概念)

简单的说Node.js就是运行在服务端的JavaScript,起初段定位是后端开发语言,由于技术的不够成熟,一般小型项目会完全使用node.js作为后台支撑,大项目中,运行不够稳定,不会轻易使用...

干货 | 如何利用Node.js 构建分布式集群

引言在软件定义的世界里,企业通过Web应用和移动应用程序来提供大部分的服务,Node.js迅速成为时下最为流行的一个平台之一,就和它可以搭建响应速度快、易于扩展的web应用和移动应用有很大关系,并凭...

nodejs mongodb 实现简易留言板(node.js留言板)

一个朋友问了一下mongodb的一些操作问题我就做了下面这个简单的留言板给他做一个实例希望能帮助到他express的框架就不说了express的问题请移步nodejs之expressht...

nodejs mqtt 智能售货机系统物联网控制系统源码分享

智能售货机系统(Moleintelligentvendingmachinesystem)是一套物联网控制系统性的解决方案。主要涉及到的语言和库有c,c++,js,nodejs,vue.js,...

为什么 Node.js 这么火,而同样异步模式 Python 框架 Twisted 却十几年一直不温不火?

说nodejs只是靠营销的是否太天真了些?当初nodejs出来的时候各种BUG,我简单的测试其大文件传输都会出现各种问题。而同時期的其他阵营早就甩其几条街了。但是为什么却能一直不断发展壮大?...

2020年14个最有用的NodeJS库(node用什么数据库)

Express快速,简单,极简的节点Web框架对…有好处·易于处理多种类型的请求,例如GET,PUT,POST和DELETE请求·快速构建单页,多页和混合Web应用程序每周下载1100万Lice...

连载:2016年最好的JS框架和库(下)

继续上一期的介绍:Agility.jsAgility.js是专为JS服务的MVC库,你可以免费编写可再用和可维护的浏览器代码,Agility支持Js,样式(CSS)、内容(HTML)和行为(JS)。C...

awesome-nodejs 终极资源库:60K+星标的开发者宝藏

Node.js终极资源库:60K+星标的开发者宝藏引言在GitHub上,有一个备受瞩目的Node.js资源仓库,以其惊人的60.6k星标量和6kfork量,成为了Node.js开发者的必备参考。这个...

取消回复欢迎 发表评论: