概述
前面思路篇和基础篇我们已经准备得差不多了,现在开始正式进入到功能实现步骤。第一步,是处理博客初始化工作。
这一步我们将完成博客的初始化工作,回写配置信息、注册管理员账号等操作。
当我们需要将博客分发和部署到服务器的时候,最好的操作是,运行博客程序后,类似其他cms、WordPress一样,初次访问,会要求输入数据库信息和管理员账号信息,通过简单的配置后,不可就可以开始正常使用了。我们也一样,也在博客开始运行的时候,来配置数据库信息、管理员账号。
定义常量
在开始初始化之前,我先定义几个常量,在config文件夹下,创建一个constant.go 文件,并在里面定义常量:
package config
const StatusOK = 0
const StatusFailed = -1
const StatusNoLogin = 1001
golang的常量使用const关键词来定义,我们现在定义了3中状态,StatusOK = 0
代表正常状态,值为0;StatusFailed = -1
代表错误状态,包括各种错误,这里不做细分,实际错误信息以文字直接显示,错误代码值为-1;StatusNoLogin = 1001
代表未登录状态,未登录有点特殊,采用一个特殊的状态,可以明确告知前端可以在报错后引导到登录页面来处理登录信息。这些状态常量,我们将会在后续的开发中逐一使用到。下面我们开始初始化工作。
初始化界面html代码
我们在template目录下新建install目录,并创建一个index.html文件,写入下面的代码:
{% include "partial/header.html" %}
<div class="layui-container">
<div class="install">
<div class="layui-card">
<div class="layui-card-header">{{SiteName}}初始化</div>
<div class="layui-card-body">
<form class="layui-form" οnsubmit="return false;">
<div id="mysql-config" style="display: block;">
<div class="layui-form-item">
<label class="layui-form-label">数据库地址</label>
<div class="layui-input-inline">
<input type="text" name="host" required lay-verify="required" placeholder="一般是localhost" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">数据库端口</label>
<div class="layui-input-inline">
<input type="text" name="port" required lay-verify="required" placeholder="一般是3306" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">数据库名称</label>
<div class="layui-input-inline">
<input type="text" name="database" required lay-verify="required" placeholder="安装到哪个数据库" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">数据库用户</label>
<div class="layui-input-inline">
<input type="text" name="user" required lay-verify="required" placeholder="填写数据库用户名" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">数据库密码</label>
<div class="layui-input-inline">
<input type="password" name="password" required lay-verify="required" placeholder="填写数据库密码" autocomplete="off" class="layui-input">
</div>
</div>
</div>
<hr>
<div class="layui-form-item">
<label class="layui-form-label">后台用户名</label>
<div class="layui-input-inline">
<input type="text" name="admin_user" required lay-verify="required" placeholder="用于登录管理博客" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">后台密码</label>
<div class="layui-input-inline">
<input type="password" name="admin_password" required lay-verify="required" placeholder="登录密码" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="install">确认初始化</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% include "partial/footer.html" %}
这里的代码包含了用户要输入的数据库地址、数据库端口、数据库名称、数据库用户、数据库密码等数据库信息,还要求用户输入管理员账号、管理员密码,来准备做后期博客管理账号使用。这里模板同样地使用了include引入了模板头部、模板尾部代码片段。
提交表单的js代码
前面章节我们交代了,为了方便js统一管理,我们将js代码存放在public/static/js/app.js 文件中。因为我们前端使用的是layui框架,因此form表单提交,也是需要使用layui的form表单提交组件来完成提交。我们在app.js 中, 最后的});
前增加一行,并加入如下代码来实现:
//监听提交
form.on('submit(install)', function(data){
let postData = data.field;
postData.port = Number(postData.port)
$.post("/install", postData, function (res) {
if(res.code === 0) {
layer.alert(res.msg, function(){
window.location.href = "/";
});
} else {
layer.msg(res.msg);
}
}, 'json');
return false;
});
这段js的作用是,监听用户点击确认初始化按钮,因为我们在html中,给确认初始化按钮添加了lay-submit
属性和 lay-filter="install"
属性。<button class="layui-btn" lay-submit lay-filter="install">确认初始化</button>
。
在页面提交前,我们还需要注意,port字段是整形,在提交之前,我们需要先转换它成为整形postData.port = Number(postData.port)
,否则,golang是强类型语言,直接按字符串格式提交到后台的话,它会无法解析而报错。
我们通过post方式提交,并定义返回的格式为json,方便直接处理。如果返回的数据res中,res.code为0,也就是上面我们定义的const StatusOK = 0
,则表示安装完毕,跳转到首页,否则就弹出一个超时5秒自动消失的消息layer.msg(res.msg);
来展示后端返回的错误信息。
初始化逻辑处理
我们接着在controller文件夹下,创建一个install.go文件,用来存放处理初始化过程的控制器。在install.go文件里,我们需要创建2个控制器函数,一个是用于显示初始化界面的控制器函数Install(),一个是用于接收处理初始化表单提交结果的控制器函数InstallForm()。
显示初始化界面的控制器函数Install()
func Install(ctx iris.Context) {
if config.DB != nil {
ctx.Redirect("/")
return
}
ctx.View("install/index.html")
}
这里的判断很简单,判断数据库信息是否已经存在了,如果已经存在的话,表示已经初始化过了不能再次初始化,我们使用ctx.Redirect("/")
跳转到首页,如果还未初始化过,则显示初始化界面信息。
定义接收初始化变量的结构体
我们在接收数据之前,先定义一个我们需要接收的变量的结构体。我们在request文件夹下,创建一个install.go文件,用来存放接收初始化变量的结构体代码:
package request
type Install struct {
Database string `form:"database" validate:"required"`
User string `form:"user" validate:"required"`
Password string `form:"password" validate:"required"`
Host string `form:"host" validate:"required"`
Port int `form:"port" validate:"required"`
AdminUser string `form:"admin_user" validate:"required"`
AdminPassword string `form:"admin_password" validate:"required"`
}
这个结构体,我们定义了Database
、User
、Password
、Host
、Port
、AdminUser
、AdminPassword
这几个我们上面表单中存在字段,我们采用form表单形式提交数据,因此我们这里对结构体字段的标记为form
,同时这些字段,都是必填字段,因此,我们在结构体字段的标记为validate:"required"
。我们定义了对应的form字段,下面接收数据的时候,就可以将前端表单提交的数据都读入到这个结构体中了。
接收处理初始化表单提交结果的控制器函数InstallForm()
if config.DB != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": "Failed",
})
return
}
var req request.Install
if err := ctx.ReadForm(&req); err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
config.JsonData.DB.Database = req.Database
config.JsonData.DB.User = req.User
config.JsonData.DB.Password = req.Password
config.JsonData.DB.Host = req.Host
config.JsonData.DB.Port = req.Port
err := config.InitDB(&config.JsonData.DB)
if err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
err = config.WriteConfig()
if err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
//创建管理员
err = provider.InitAdmin(req.AdminUser, req.AdminPassword)
if err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
ctx.JSON(iris.Map{
"code": config.StatusOK,
"msg": fmt.Sprintf("%s初始化成功", config.ServerConfig.SiteName),
})
这一部分,同样的,需要先检查网站是否已经初始化过了,如果已经初始化过,则不能再次初始化,返回一个错误信息给前端。
当博客没有初始化过的话,我们就声明一个req变量,来接收用户提交的表单数据:
var req request.Install
if err := ctx.ReadForm(&req); err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
这里的判断是,如果用户提交的数据跟我们定义的一样,可以完整读入req变量的话,就继续,不能成功读入req变量的话,就返回一个错误信息。
接着,我们将接收到的mysql数据,赋值给配置变量,然后通过InitDB来验证这些提交的信息是否正确,如果正确,执行InitDB的逻辑代码,如果不正确,则返回一个错误。
config.JsonData.DB.Database = req.Database
config.JsonData.DB.User = req.User
config.JsonData.DB.Password = req.Password
config.JsonData.DB.Host = req.Host
config.JsonData.DB.Port = req.Port
err := config.InitDB(&config.JsonData.DB)
if err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
当提交的数据库信息验证通过后,我们需要将接收到的数据库信息,回写到配置文件中:
err = config.WriteConfig()
if err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
WriteConfig()函数我们还没有定义,我们还需要到config中定义一下。我们现在打开config.go文件,添加WriteConfig函数:
func WriteConfig() error {
//将现有配置写回文件
configFile, err := os.OpenFile(fmt.Sprintf("%sconfig.json", ExecPath), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer configFile.Close()
buff := &bytes.Buffer{}
buf, err := json.MarshalIndent(JsonData, "", "t")
if err != nil {
return err
}
buff.Write(buf)
_, err = io.Copy(configFile, buff)
if err != nil {
return err
}
return nil
}
这个函数的意思是,读取项目根目录下的config.json文件,采用可写方式来读取,如果文件不存在的话,自动创建。然后将JsonData
配置信息转换成json,再将json回写入congif.json文件。期间如果出错的话,比如文件创建不成功、json转换失败、文件写入不成功等错误的时候,就返回错误信息。
完成数据回写后,我们还需要根据用户提交的管理员账号和管理员密码,创建一个管理员用来做后续的登录管理操作:
//创建管理员
err = provider.InitAdmin(req.AdminUser, req.AdminPassword)
if err != nil {
ctx.JSON(iris.Map{
"code": config.StatusFailed,
"msg": err.Error(),
})
return
}
创建管理员的操作是跟model模型打交道的,在控制器到模型直接,我们再增加了一个中间层,这一层我们命名为provider。我们在provider文件夹加创建一个admin.go文件,用来处理创建管理员等操作逻辑。这里,我们先在admin.go中,增加一个InitAdmin()函数:
func InitAdmin(userName string, password string) error {
if userName == "" || password == "" {
return errors.New("请提供用户名和密码")
}
var exists int64
db := config.DB
db.Model(&model.Admin{}).Count(&exists)
if exists > 0 {
return errors.New("已有管理员不能再创建")
}
admin := &model.Admin{
UserName: userName,
Status: 1,
}
admin.Password = admin.EncryptPassword(password)
err := admin.Save(db)
if err != nil {
return err
}
return nil
}
这个函数接收2个参数,一个是userName,一个是password。只有两个都提供的时候,才可以创建管理员账号,同样地,为了管理方便,简化博客管理流程,整个博客只允许创建一个管理员,如果管理员已经存在了,则不能再次创建。
var exists int64
db := config.DB
db.Model(&model.Admin{}).Count(&exists)
if exists > 0 {
return errors.New("已有管理员不能再创建")
}
因为管理员有密码,但往往,我们建立网站的话,密码是不会明文存储的,因此,我们还需要增加一个密码加密函数EncryptPassword
。我们将密码加密函数EncryptPassword
存放在model/admin.go文件中:
func (admin *Admin) EncryptPassword(password string) string {
if password == "" {
return ""
}
pass := []byte(password)
hash, err := bcrypt.GenerateFromPassword(pass, bcrypt.MinCost)
if err != nil {
return ""
}
return string(hash)
}
密码加密我们使用golang官方加密包golang.org/x/crypto/bcrypt
。这样我们将得到一个被加密后的密码字符串。我们将这个字符串写入到数据库中,以方便以后登录的时候做比对。
最后,就是调用admin.Save(db)
来将管理员信息写入数据库了。
配置初始化路由
初始化逻辑我们写完了,我们还需要将初始化的两个控制器注册到路由中,才能在浏览器中访问到。我们现在打开route/base.go文件,在Register中增加两个路由:
app.Get("/install", controller.Install)
app.Post("/install", controller.InstallForm)
添加完毕的base.go文件如下:
package route
import (
"github.com/kataras/iris/v12"
"irisweb/controller"
)
func Register(app *iris.Application) {
app.OnErrorCode(iris.StatusNotFound, controller.NotFound)
app.OnErrorCode(iris.StatusInternalServerError, controller.InternalServerError)
app.Get("/", controller.IndexPage)
app.Get("/install", controller.Install)
app.Post("/install", controller.InstallForm)
}
至此,整个博客的mysql初始化、管理员用户注册就完成了。
验证结果
为了验证初始化界面,我们先将根目录下的config.json删掉,模拟未初始化状态。接着我们运行下看看,如果不出意外的话,我们将看到如下界面效果:
完整的项目示例代码托管在GitHub上,需要查看完整的项目代码可以到github.com/fesiong/goblog 上查看,也可以直接fork一份来在上面做修改。
最后
以上就是拼搏日记本为你收集整理的博客功能篇:博客的初始化处理和管理员的账号注册的全部内容,希望文章能够帮你解决博客功能篇:博客的初始化处理和管理员的账号注册所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复