Go-web开发快速入门——一、gin+gorm完成用户注册登录及用数据库存储用户信息(附源码,超详细)

本篇博客是Go-web开发快速入门系类的第一篇,本篇博客将介绍如何使用gin和gorm实现用户注册登录及用数据可存储用户信息。

简单来说,gin用于web开发,获取用户信息,而gorm用于连接数据库,将用户数据迁移到我们的数据库中进行存储

参考资料:

gin中文文档

gorm中文文档

一、准备

在开始我们的项目之前,首先确保你已安装了Go语言并配置好其环境并拥有Go语言编辑器,同时也要有数据库相关的工具,其次,为了方便调试,可安装相应的接口测试工具和数据库可视化工具。

本项目所使用的所有工具如下:

  • Visual Studio Code —— 编写并运行Go程序
  • MySql —— 建立数据库
  • Postman —— 接口测试
  • Navicat Premium —— 数据库可视化

在敲代码之前,我们先把项目模块化并引入相关的依赖

1.新建一个项目文件夹

2.使用go mod初始化项目模块

go mod init 项目模块名称

在这里插入图片描述

执行完毕后项目下会出现一个go.mod文件,描述项目使用的模块名称及go的版本
在这里插入图片描述

3.安装gin和gorm

在终端输入以下代码:

go get -u github.com/gin-gonic/gin
go get -u github.com/jinzhu/gorm

安装完成后,可以发现go.mod文件更新了项目所需的依赖

在这里插入图片描述

4.引入mysql

在终端输入以下代码:

go get github.com/go-sql-driver/mysql

5.在mysql中创建一个数据库

在终端输入mysql -u 你的mysql用户名 -p并输入密码即可进入mysql
输入create database 数据库名即可创建数据库

在这里插入图片描述

二、编写Go程序

新建一个main.go文件

首先构建我们的用户结构体,由于要迁移到数据库,必须引入gorm.Model和相关的描述

type User struct {
	gorm.Model
	Name      string `gorm:"varchar(20);not null"`
	Telephone string `gorm:"varchar(20);not null;unique"`
	Password  string `gorm:"size:255;not null"`
}

然后创建路由引擎

func main() {

	//创建一个默认的路由引擎
	r := gin.Default()

	//在9090端口启动服务
	panic(r.Run(":9090"))
}

我们在127.0.0.9090/register和127.0.0.1.9090/login实现用户的注册和登录,将注册和登录的部分下入main函数的两条语句之间

	//注册
	r.POST("/register", func(ctx *gin.Context) {

		//获取参数
		name := ctx.PostForm("name")
		telephone := ctx.PostForm("telephone")
		password := ctx.PostForm("password")

		//数据验证
		if len(name) == 0 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "用户名不能为空",
			})
			return
		}
		if len(telephone) != 11 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "手机号必须为11位",
			})
			return
		}
		if len(password) < 6 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "密码不能少于6位",
			})
			return
		}

		//判断手机号是否存在
		var user User
		db.Where("telephone = ?", telephone).First(&user)
		if user.ID != 0 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "用户已存在",
			})
			return
		}

		//创建用户
		hasedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    500,
				"message": "密码加密错误",
			})
			return
		}
		newUser := User{
			Name:      name,
			Telephone: telephone,
			Password:  string(hasedPassword),
		}
		db.Create(&newUser)

		//返回结果
		ctx.JSON(http.StatusOK, gin.H{
			"code":    200,
			"message": "注册成功",
		})
	})
	//登录
	r.POST("/login", func(ctx *gin.Context) {

		//获取参数
		telephone := ctx.PostForm("telephone")
		password := ctx.PostForm("password")

		//数据验证
		if len(telephone) != 11 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "手机号必须为11位",
			})
			return
		}
		if len(password) < 6 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "密码不能少于6位",
			})
			return
		}

		//判断手机号是否存在
		var user User
		db.Where("telephone = ?", telephone).First(&user)
		if user.ID == 0 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "用户不存在",
			})
			return
		}

		//判断密码是否正确
		if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "密码错误",
			})
		}

		//返回结果
		ctx.JSON(http.StatusOK, gin.H{
			"code":    200,
			"message": "登录成功",
		})
	})

接着构建我们的数据库,编写InitDB()函数

func InitDB() *gorm.DB {
	driverName := "mysql"
	host := "127.0.0.1"
	port := "3306"
	database := "你的数据库名"
	username := "你的mysql用户名"
	password := "你的mysql密码"
	charset := "utf8"
	args := fmt.Sprintf("%s:%s@(%s:%s)/%s?charset=%s&parseTime=true",
		username,
		password,
		host,
		port,
		database,
		charset)

	db, err := gorm.Open(driverName, args)
	if err != nil {
		panic("failed to connect database, err:" + err.Error())
	}

	//迁移
	db.AutoMigrate(&User{})

	return db

}

最后在main函数开头引入InitDB()函数,并延迟关闭数据库,在main函数开头写入:

	//获取初始化的数据库
	db := InitDB()
	//延迟关闭数据库
	defer db.Close()

完整代码如下:

package main

import (
	"fmt"
	"net/http"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
	"golang.org/x/crypto/bcrypt"
)

type User struct {
	gorm.Model
	Name      string `gorm:"varchar(20);not null"`
	Telephone string `gorm:"varchar(20);not null;unique"`
	Password  string `gorm:"size:255;not null"`
}

func main() {

	//获取初始化的数据库
	db := InitDB()
	//延迟关闭数据库
	defer db.Close()

	//创建一个默认的路由引擎
	r := gin.Default()

	//注册
	r.POST("/register", func(ctx *gin.Context) {

		//获取参数
		name := ctx.PostForm("name")
		telephone := ctx.PostForm("telephone")
		password := ctx.PostForm("password")

		//数据验证
		if len(name) == 0 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "用户名不能为空",
			})
			return
		}
		if len(telephone) != 11 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "手机号必须为11位",
			})
			return
		}
		if len(password) < 6 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "密码不能少于6位",
			})
			return
		}

		//判断手机号是否存在
		var user User
		db.Where("telephone = ?", telephone).First(&user)
		if user.ID != 0 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "用户已存在",
			})
			return
		}

		//创建用户
		hasedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    500,
				"message": "密码加密错误",
			})
			return
		}
		newUser := User{
			Name:      name,
			Telephone: telephone,
			Password:  string(hasedPassword),
		}
		db.Create(&newUser)

		//返回结果
		ctx.JSON(http.StatusOK, gin.H{
			"code":    200,
			"message": "注册成功",
		})
	})

	//登录
	r.POST("/login", func(ctx *gin.Context) {

		//获取参数
		telephone := ctx.PostForm("telephone")
		password := ctx.PostForm("password")

		//数据验证
		if len(telephone) != 11 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "手机号必须为11位",
			})
			return
		}
		if len(password) < 6 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "密码不能少于6位",
			})
			return
		}

		//判断手机号是否存在
		var user User
		db.Where("telephone = ?", telephone).First(&user)
		if user.ID == 0 {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "用户不存在",
			})
			return
		}

		//判断密码是否正确
		if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{
				"code":    422,
				"message": "密码错误",
			})
		}

		//返回结果
		ctx.JSON(http.StatusOK, gin.H{
			"code":    200,
			"message": "登录成功",
		})
	})

	//在9090端口启动服务
	panic(r.Run(":9090"))
}

func InitDB() *gorm.DB {
	driverName := "mysql"
	host := "127.0.0.1"
	port := "3306"
	database := "你的数据库名"
	username := "你的mysql用户名"
	password := "你的mysql密码"
	charset := "utf8"
	args := fmt.Sprintf("%s:%s@(%s:%s)/%s?charset=%s&parseTime=true",
		username,
		password,
		host,
		port,
		database,
		charset)

	db, err := gorm.Open(driverName, args)
	if err != nil {
		panic("failed to connect database, err:" + err.Error())
	}

	//迁移
	db.AutoMigrate(&User{})

	return db

}

三、测试

用postman进行接口测试

点击"+"新建用于测试的request

在这里插入图片描述

输入相应的url发送请求,得到如下结果:

在这里插入图片描述

在这里插入图片描述

用navicat连接数据库可视化显示

填写相关信息连接数据库,用户名为MySql的用户名,密码为MySql的密码
在这里插入图片描述

连接后可看到数据库的内容如下:

在这里插入图片描述

在vs code终端有如下显示

在这里插入图片描述

四、重构与代码优化

为了使代码更好的复用,我们对代码进行重构,重构的结构如下:

在这里插入图片描述

model/user.go,用于定义我们的用户

package model

import "github.com/jinzhu/gorm"

type User struct {
	gorm.Model
	Name      string `gorm:"varchar(20);not null"`
	Telephone string `gorm:"varchar(20);not null;unique"`
	Password  string `gorm:"size:255;not null"`
}

common/database 用于数据库的构建与连接

package common

import (
	"demo/model"
	"fmt"

	"github.com/jinzhu/gorm"
)

var DB *gorm.DB

func InitDB() *gorm.DB {
	driverName := "mysql"
	host := "127.0.0.1"
	port := "3306"
	database := "你的数据库名称"
	username := "你的mysql用户名"
	password := "你的mysql密码"
	charset := "utf8"
	args := fmt.Sprintf("%s:%s@(%s:%s)/%s?charset=%s&parseTime=true",
		username,
		password,
		host,
		port,
		database,
		charset)

	db, err := gorm.Open(driverName, args)
	if err != nil {
		panic("failed to connect database, err:" + err.Error())
	}

	//迁移
	db.AutoMigrate(&model.User{})

	DB = db

	return db

}

func GetDB() *gorm.DB{
	return DB
}

controller/userController 用于编写用户相关的函数,如注册、登录

package controller

import (
	"demo/common"
	"demo/model"
	"net/http"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"
)

func Register(ctx *gin.Context) {

	db := common.GetDB()

	//获取参数
	//此处使用Bind()函数,可以处理不同格式的前端数据
	var requestUser model.User
	ctx.Bind(&requestUser)
	name := requestUser.Name
	telephone := requestUser.Telephone
	password := requestUser.Password

	//数据验证
	if len(name) == 0 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "用户名不能为空",
		})
		return
	}
	if len(telephone) != 11 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "手机号必须为11位",
		})
		return
	}
	if len(password) < 6 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "密码不能少于6位",
		})
		return
	}

	//判断手机号是否存在
	var user model.User
	db.Where("telephone = ?", telephone).First(&user)
	if user.ID != 0 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "用户已存在",
		})
		return
	}

	//创建用户
	hasedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    500,
			"message": "密码加密错误",
		})
		return
	}
	newUser :=model.User{
		Name:      name,
		Telephone: telephone,
		Password:  string(hasedPassword),
	}
	db.Create(&newUser)

	//返回结果
	ctx.JSON(http.StatusOK, gin.H{
		"code":    200,
		"message": "注册成功",
	})
}


func Login(ctx *gin.Context) {

	db := common.GetDB()

	//获取参数
	//此处使用Bind()函数,可以处理不同格式的前端数据
	var requestUser model.User
	ctx.Bind(&requestUser)
	telephone := requestUser.Telephone
	password := requestUser.Password
	
	//数据验证
	if len(telephone) != 11 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "手机号必须为11位",
		})
		return
	}
	if len(password) < 6 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "密码不能少于6位",
		})
		return
	}
	
	//判断手机号是否存在
	var user model.User
	db.Where("telephone = ?", telephone).First(&user)
	if user.ID == 0 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "用户不存在",
		})
		return
	}
	
	//判断密码是否正确
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{
			"code":    422,
			"message": "密码错误",
		})
	}
	
	//返回结果
	ctx.JSON(http.StatusOK, gin.H{
		"code":    200,
		"message": "登录成功",
	})
}

routes.go 用于存放我们的路由

package main

import (
	"demo/controller"

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

func CollectRoutes(r *gin.Engine) *gin.Engine {

	//注册
	r.POST("/register", controller.Register)
	//登录
	r.POST("/login", controller.Login)

	return r

}

main.go 用于数据库初始化、路由与服务的启动

package main

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

func main() {

	//获取初始化的数据库
	db := common.InitDB()
	//延迟关闭数据库
	defer db.Close()

	//创建一个默认的路由引擎
	r := gin.Default()

	//启动路由
	CollectRoutes(r)

	//在9090端口启动服务
	panic(r.Run(":9090"))
}

注意在重构之后,运行时需要同时运行main.go和routes.go

go run main.go routes.go

或同时编译main.go和routes.go再运行main.exe

go build main.go routes.go

main.exe

测试一下,重构后的代码正常运行~

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

下一篇博客将介绍如何使用jwt配合中间件进行用户认证~
Go-web开发快速入门——二、使用jwt配合中间件进行用户认证

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值