之前我们完成了注册,接下来要完成登录功能,但是有个问题,我们的数据没有持久化,无法得知有哪些用户注册了,这样就无法在登录时进行校验。

所以接下来我们要将数据持久化,也就是存储到数据库中,这里使用 MySQL

💾建立数据库

首先我们要新建一个库,用来存放项目数据,建立一个名为 ginhello 的库。

create database ginhello;

当我们建立好数据库时,就可以通过 Goland进行数据库连接和使用。

选择 Goland 右侧的 Database,,点击 + 号,选择 MySQL。填写相关信息,账户名,密码,要连接的数据库库名等信息,进行连接。

之后我们就可以通过 Goland 的图形化界面去新建立表。

右键选择刚刚连接的 ginhello ,new , table 然后填写字段名称和属性。下面给出建表语句。

create table user
(
	id int auto_increment primary key,
	email varchar(30) not null,
	password varchar(40) not null
)
comment '用户表';

🔗连接数据库

数据库的表已经建立成功,那么省下来就该与我们的 Gin 建立联系,通过代码连接到数据库。

新建文件夹 initDB 新建 initDB.go

package initDB

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

var Db *sql.DB

func init() {
	var err error
	Db, err = sql.Open("mysql", "root:1234@tcp(127.0.0.1:3306)/ginhello")
	if err != nil {
		log.Panicln("err:", err.Error())
	}
	Db.SetMaxOpenConns(10)
	Db.SetMaxIdleConns(10)
}

首先新建一个全局变量sql.DB方便我们后期调用,然后通过 sql.Open对数据进行连接。SetMaxOpenConns 是对 最大链接数的设置 SetMaxIdleConns 是设置最大空闲链接数

经过简单的设置,我们的程序就可以连接到数据库。

👩增加用户

当数据库连接上时,就可以完善我们的注册功能。

修改 userModel.gouser 添加新的方法。

func (user *UserModel) Save() int64 {
	result, e := initDB.Db.Exec("insert into ginhello.user (email, password) values (?,?);", user.Email, user.Password)
	if e != nil {
		log.Panicln("user insert error", e.Error())
	}
	id, err := result.LastInsertId();
	if err != nil {
		log.Panicln("user insert id error", err.Error())
	}
	return id
}

该方法是对 user 类型进行添加的方法,最后返回一个新增行id

小提示 在编写 SQL 语句时,通过ctrl+enter 快捷键组合,选择 Edit Mysql Fragment ,会在编辑器下方重写打开一个编辑界面用来写SQL,并且带有自动提示,提示内容和我们链接的数据库相关。

最后修改 UserRegister 方法,当数据校验合格的时候进行存储。

func UserRegister(context *gin.Context) {
	var user model.UserModel
	if err := context.ShouldBind(&user); err != nil {
		context.String(http.StatusBadRequest, "输入的数据不合法")
		log.Panicln("err ->", err.Error())
	}
	id := user.Save()
	log.Println("id is ", id)
	context.Redirect(http.StatusMovedPermanently, "/")
}

当都写完的时候就可以运行我们的测试 TestUserPostFormTestUserPostFormEmailErrorAndPasswordError 两个测试用例。

测试可以正常通过,此时检测我们的数据库。打开 User 表,里面只会有一条信息。

此时我们的注册也就完成了。

🔐登录

接下来快速完成登录功能。

具体的前端代码请看 Github下面贴出来具体的后端代码

我们要根据 EmailPassword 校验用户数据是否正确。

userModel.go 根据 Email 查询代码,该文件其他代码也有略微变化,请注意查看。

func (user *UserModel) QueryByEmail() UserModel {
	u := UserModel{}
	row := initDB.Db.QueryRow("select * from user where email = ?;", user.Email)
	e := row.Scan(&u.Id, &u.Email, &u.Password)
	if e != nil {
		log.Panicln(e)
	}
	return u
}

通过对比页面上传入的密码和数据库中的密码是否一致来确定用户是否可以登录。

userHandler.go

func UserLogin(context *gin.Context) {
	var user model.UserModel
	if e := context.Bind(&user); e != nil {
		log.Panicln("login 绑定错误", e.Error())
	}

	u := user.QueryByEmail()
	if u.Password == user.Password {
		log.Println("登录成功", u.Email)
		context.HTML(http.StatusOK, "index.tmpl", gin.H{
			"email": u.Email,
		})
	}
}

每当一个功能编写完成的时候,切记要写上单元测试,这将会帮助我们在日后的项目中更加完善。

用户登录测试。

func TestUserLogin(t *testing.T) {
	email := "youngxhui@163.com"
	value := url.Values{}
	value.Add("email", email)
	value.Add("password", "1234")
	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodPost, "/user/login", bytes.NewBufferString(value.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded; param=value")
	router.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, strings.Contains(w.Body.String(), email), true)
}

注意 该部分代码交之前有较大的改动,删除了之前部分演示代码。

✍总结

本节通过链接数据库,完成了数据库的增加和查询功能,并且完成了注册和登录的相关功能。

👩‍💻本章节代码

Github