基于Golang的开发框架Gin实战

这一系列文章主要是让想进阶服务端开发的前端小伙伴们一个入门参考,后端大佬能够不用看了,极可能是浪费你的时间。css

上一篇文章基于Golang的微服务——Micro实践(二)遇到问题了,我得先理解下,就当是占了个坑吧,后面我理解透了再补上。 这篇记录下我尝试Gin框架遇到的问题。html

Gin中文文档前端

学习Golang的过程当中,虽而后端语言的大体思路是差很少的,可是在语言细节和语法上跟以前接触的Laravel, Node.js仍是有区别的.多熟悉,多对比,更容易知道其因此然了。mysql

Gin 是什么?

Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。类比与laravel

Node.js的web框架: express, koagit

PHP的web框架:laravel, lumen , yii程序员

Python的web框架 Django , Flaskgithub

只要你以前接触过这些,这么一类比你确定就秒懂了。web

官网右上角能够切换语言 sql

Gin安装

Golang开发环境参照我前面写的基于Golang的微服务——上手篇文章配置就行

新建项目目录 tech, 我以前写Laravel的时候感受代码和项目结构真的很优雅,就按照Laravel的项目结构来了。

tech
    -app
        -Controllers // 控制器函数
        -Middleware
        -Models //  数据模型
    -config // 配置文件
    -databse // 数据库
        -mysql.go
    -public // 静态资源目录
        -css
        -imgs
        -js
    -resources // 项目资源,好比放scss,经过编译转化成项目用的css
        -lang
        -sass
        -js
    -routes // 路由
        -web.go
    -views // 视图文件,这个项目会涉及到后端模板渲染和 API开发
        -usr
            -index.tmpl
        -post
            -index.tmpl
        -home
            -index.tmpl
    .env // 配置文件
    .gitignore 
    main.go //  入口文件
    README.md
复制代码

附一张项目结构图:

项目包依赖用的 govender,具体用法我前面的文章有写到。也能够本身查查资料。

入口文件

main.go

package main

import (
	"os"
	_ "tech/config"
	_ "tech/database"
	"tech/routes"
)

func main()  {
	r := routes.InitRouter()
	port := os.Getenv("HTTP_PORT")
	r.Run(":" + port) // 监听并在 0.0.0.0:8080 上启动服务
}
复制代码

先贴下入口文件里的内容,而后根据入口文件从上到下讲解。

配置文件

_ tech/config

这里导入的是配置文件,tech/config/config.go文件源码以下:

package config

import (
	"github.com/joho/godotenv"
	"log"
	"os"
)

func init(){
	err := godotenv.Load() // 载入 godotenv
	if err != nil {
		log.Fatal("Error loading .env file")
	}

	PORT := os.Getenv("HTTP_PORT") //  获取.env配置文件里的HTTP_POTRT值
	log.Print(PORT)
}
复制代码

这里涉及到了一个 本地.env 文件读取的包godotenv,能够获取到项目里.env文件预设置的值

// 这里是 .env文件内容
HTTP_PORT=8090
AUTHOR=winyh
复制代码

这里经过简单的配置文件演示项目的一些配置,好比项目端口号,数据库开发环境的数据库信息,均可以放到.env 若是正式环境的数据库信息放在.env里就必定要注意了,把这个配置文件添加到.gitingore忽略文件里,避免上传到远程代码仓库了。

数据库文件

_ tech/database

这里是数据库连接功能的实现,tech/database/mysql.go源码以下:

package database

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql" //加载mysql驱动
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var DB *gorm.DB

func init()  {
	var err error
	DB, err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/tech?charset=utf8&parseTime=True&loc=Local&timeout=10ms")

	if err != nil {
		fmt.Printf("mysql connect error %v", err)
	}

	if DB.Error != nil {
		fmt.Printf("database error %v", DB.Error)
	}
}
复制代码

项目数据库选的是mysql,因此须要先安装mysql的驱动包

go get github.com/go-sql-driver/mysql
复制代码

写原生sql语句台痛苦了,因此引入一个ORM(简单粗暴的理解为一系列的原生sql 语句的封装,可让你操做数据库更简便,会想起以前写Larvel,它自带的ORM用着是真 爽,基本上是要啥有啥)

go get github.com/jinzhu/gorm
复制代码

数据库的连接主要是下面这句,其中

root:是你本机的mysql用户名 123456: 是你本机的数据库密码 127.0.0.1:3306:表示数据库地址和端口号 tech:是你本机创建的数据库名称

DB, err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/tech?charset=utf8&parseTime=True&loc=Local&timeout=10ms")
复制代码

路由文件

tech/routes

入口文件里引入了routes这个包,tech/routes/web.go源码以下:

package routes

import (
	"github.com/gin-gonic/gin"
	"tech/app/Controllers"
)

func InitRouter() *gin.Engine {
	r := gin.Default()

	r.Static("/public", "./public") // 静态文件服务
	r.LoadHTMLGlob("views/**/*") // 载入html模板目录

	// web路由
	r.GET("/", Controllers.Home)
	r.GET("/about", Controllers.About)
	r.GET("/post/index", Controllers.Post)

	// 简单的路由组: v1
	v1 := r.Group("/api")
	{
		v1.GET("/ping", Controllers.Ping)
		v1.POST("/user/create", Controllers.UserCreate)
		v1.POST("/user/delete", Controllers.UserDestroy)
		v1.POST("/user/update", Controllers.UserUpdate)
		v1.POST("/users", Controllers.UserFindAll)
	}

	return r
}
复制代码

后端项目基本都是MVC的模式,微服务架构可能会更简单一点。路由是一个项目对外提供服务的入口,全部的前端请求从这里进入到指定的处理函数。处理函数会操做业务逻辑,返回数据和给出响应,完成一次请求。 这里引入的包"github.com/gin-gonic/gin"就是Gin框架的包。其次引入了咱们本身编写的本地包"tech/app/Controllers"

我把这个路由文件里实现的功能分为了两类,一类是提供web服务的路由(也就是返回模板渲染后的html文件),一类是提供 Restful API 服务的路由。路由地址匹配咱们的处理函数。

控制器

上面每一个路由地址都关联有一个处理函数,咱们把它叫作控制器。讲解其中的 Controllers/Home.go控制器内容,源码以下:

package Controllers

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

func Home(c *gin.Context) {
	c.HTML(http.StatusOK, "home/index.tmpl", gin.H{
		"title": "这是首页",
	})
}
复制代码

基本逻辑是获取到请求的上下文,用Gin提供的c.HTML返回须要渲染返回到前台的数据。这里的title数据能够手动定义,也能够在数据库里查询到后再放入模板文件里渲染,这样看到的数据就是咱们数据库里存放的数据了。

视图

视图是返回给前端的页面模板渲染文件,后端从数据库查询到的或者自定义的数据,均可以经过变量的方式渲染到模板里面,视图从后端到前端可见会经历两次渲染,第一次是后端把数据填充进去,而后用模板引擎把模板文件渲染成html,发送到前端的时候,浏览器识别到 content-Type:'text/html',再次经过浏览器的渲染,把html标签渲染成用户可见的界面。第一次渲染是在服务器渲染的,第二次渲染是在前端渲染的。参照这里你应该能够理解前端常说的为了作SEO优化,须要用Node.js作服务端渲染是什么个意思可,能够参照我以前写的服务端渲染文章。 这里贴一下 views/home/index.tmpl 模板文件的源码:

{{ define "home/index.tmpl" }}
    <html>
        <head>
            <meta charset="UTF-8">
            <title>Gin 框架测试</title>
        </head>

        <body>
        <div class="container">
            <h1>这是一个测试页面</h1>
            <p>{{ .title }}</p>
        </div>
        </body>
    </html>
{{ end }}
复制代码

再列举一个 API服务的控制器,Controllers/Api.go源码以下:

package Controllers

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
	"net/http"
	"tech/app/Models"
)

type Admins struct {
	gorm.Model
	Name string `json:"name"  binding:"required"`
	Password string `json:"password"  binding:"required"`
	Mobile string `json:"mobile" binding:"required"`
}

func Ping(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "pong",
	})
}

func UserCreate(c *gin.Context) {
	var json  Models.Admins //  定义json 变量 数据结构类型 为 Models.Admins
	err := c.BindJSON(&json) //  获取前台传过来的 json数据

	if err != nil {
		fmt.Printf("mysql connect error %v", err)
	}

	id, err := json.Insert()

	if err != nil {
		fmt.Printf("database error %v", err)
		fmt.Printf("database error %v", id)
		return
	}

	c.JSON(200, gin.H{ // 反馈给前台的信息,同时返回最新建立的一条数据的Id
		"status": true,
		"id":id,
		"message":"建立成功",
	})
}

func UserDestroy(c *gin.Context)  {
	var json  Models.Admins
	err := c.BindJSON(&json)
	if err != nil {
		fmt.Printf("mysql connect error %v", err)
		return
	}

	json.Destroy()

	c.JSON(200, gin.H{
		"status": true,
		"message":"删除成功",
	})
}

func UserUpdate(c *gin.Context) {
	var json  Models.Admins
	err := c.BindJSON(&json)

	if err != nil {
		fmt.Printf("mysql connect error %v", err)
	}

	id, err := json.Update(3)

	if err != nil {
		fmt.Printf("database error %v", err)
		fmt.Printf("database error %v", id)
		return
	}

	c.JSON(200, gin.H{
		"status": true,
		"id":id,
		"message":"更新成功",
	})
}

func UserFindAll(c *gin.Context)  {
	var json  Models.Admins
	err := c.BindJSON(&json)

	if err != nil {
		fmt.Printf("mysql connect error %v", err)
	}

	result, err := json.FindAll()

	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"status":  false,
			"message": "抱歉未找到相关信息",
		})
		return
	}

	c.JSON(200, gin.H{
		"status": true,
		"data": result,
		"message":"查询成功",
	})
}
复制代码

这个文件里完成了后端基本的增删改查功能。内容相对多点,列举其中的用户新建来讲一下: UserCreate 函数会接收到前台传过来的json数据,而后 调用 id, err := json.Insert() 作数据入库的操做。这里涉及到 Insert函数,也就是接下来要降到的数据模型

数据模型

数据模型是数据的抽象,能够提供一些数据操做的函数封装。 app/Models/admin.go 源码以下:

package Models

import (
	"github.com/jinzhu/gorm"
	. "tech/database"
)

type Admins struct {
	gorm.Model  // 这里的配置可让ORM 自动维护 时间戳字段,很爽有木有
	Name string `json:"name"  binding:"required"`
	Password string `json:"password"  binding:"required"`
	Mobile string `json:"mobile" binding:"required"`
}

// Insert 新增admin用户
func (admin *Admins) Insert() (userID uint, err error) {

	result := DB.Create(&admin) //  这里的DB变量是 database 包里定义的,Create 函数是 gorm包的建立数据API
	userID = admin.ID
	if result.Error != nil {
		err = result.Error
	}
	return  // 返回新建数据的id 和 错误信息,在控制器里接收
}

// Destroy 删除admin用户
func (admin *Admins) Destroy() (err error) {

	result := DB.Delete(&admin)
	if result.Error != nil {
		err = result.Error
	}
	return
}


// Update 修改admin用户
func (admin *Admins) Update(id int64) (user Admins, err error) {
	result := DB.Model(&admin).Where("id = ?", id).Updates(&admin)
	if result.Error != nil {
		err = result.Error
	}
	return
}


// FindOne 查询admin用户
func (admin *Admins) FindAll() (admins []Admins, err error) {

	result := DB.Find(&admins) // 这里的 &admins 跟返回参数要一致

	if result.Error != nil {
		err = result.Error
		return
	}
	return
}
复制代码

在项目根目录下运行

go run main.go
复制代码

就能够在本地访问配置的路由了

localhost:8090/api/ping
复制代码

项目最后放上几个测试截图

控制台启动信息

IDE控制台里的红色输出不是错误信息哦!会把咱们项目路由和加载的模板文件都打印出来。

文章页面

用户数据查询,我这里用的是POST方法,查询接口建议你们遵循Restful Api 规则,用GET方法

数据新增,返回了当前新增数据的ID,前端端基于JSON数据格式传输交互

项目里的基本逻辑就是这样了,忽然感受写文章仍是很浪费时间的,本身写的质量也很低,权当跟你们交流吧,把本身的缺点暴露出来,有人指点交流,能学到东西就是进步。

反思了下本身,之前基本是什么技术都想学,后来发现这样不现实,要找准方向,将精力集中,让时间和努力发挥出它最大的价值而不至于分散了。

后面本身仍是主攻前端技术,深刻研究Golang 和 Node.js ,欢迎你们一块儿交流。Linux应该算是基础知识了,但愿在这个平台能够认识更多优秀的朋友。

再仔细的反思了下本身,技术栈深度不够,之后太基础的文章我可能只会本身作笔记不会写出来了,质量过低浪费了本身精力,也浪费了点击进来看的人时间,时间是多么宝贵的东西啊,于心不忍。看到一篇很不错的文章。

总结几点我很认同的观点:

  • 要去经历大多数人经历不到的,要把学习时间花在那些比较难的地方。
  • 要写文章就要写没有人写过的,或是别人写过,但我能写得更好的。
  • 更重要的是,技术和知识彻底是能够变现的。
  • 最好的 SEO 就是独一份,物以稀为贵。
  • 努力只是成功的必要条件之一,努力就会成功这句话得多思考下。

最宝贵的财富并非钱,而是你的时间。

25~35 岁是每一个人最宝贵的时光,应该用在刀刃上。

25~35 岁是每一个人最宝贵的时光,应该用在刀刃上。

25~35 岁是每一个人最宝贵的时光,应该用在刀刃上。

不会交流的计算机人员30岁之后通常会遇到不少瓶颈

原文连接左耳朵耗子:程序员如何用技术变现? 博主博客:酷壳

相关文章
相关标签/搜索