beego搭建個人部落格(一)

lightTrace發表於2018-03-25

上次我準備用Java的ssm框架來搭建一個部落格系統,我覺得後來覺得Java實在太羅嗦了,賊多的配置檔案。最近接觸了go的web開發,框架有beego,gin、revel以及iris,beego雖然很重量級,效能也差iris太遠,但是它對國人的學習是十分有好的,首先它的作者是國人,理念呢也還是參照傳統的mvc概念,加上詳盡的文件是一個非常好的入門go web的框架,所以我決定使用beego搭建一個個人部落格,前臺展示使用layui,畢竟我比較懶!

準備工作:

a.下載Sublime Text3,安裝go外掛,在mysql建立資料庫db_beego,然後匯入db_beego.sql(在github上有,連結在文章最下面),主要就是建立了使用者,文章,評論表並且插入幾條測試資料,大家隨便看下知道就可以了。

b.你的電腦肯定安裝了go環境,然後安裝beego,可以參考https://blog.csdn.net/suresand/article/details/79548796

$ go get -u github.com/astaxie/beego
$ go get -u github.com/beego/bee

直接安裝beego的框架及開發工具

或許還需要安裝go-mysql的驅動

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

c.然後在你的gopath的src目錄下執行:

bee new blog

然後看見自動建立的blog資料夾,進入blog資料夾:

bee run

訪問http://localhost:8080/出現welcome to beego畫面表示準備工作完成,然後就可以開始我們的開發了,當然由於我的水平有限,有模糊的地方請大家詳細參考beego文件https://beego.me/docs/intro/

一 配置檔案
首先配置app.conf,這個app.conf配置檔案可以做很多事,這裡我們主要配置資料庫的連結資訊:

appname = blog
httpport = 8080
runmode = dev

# MYSQL地址
dbhost = localhost

# MYSQL埠
dbport = 3306

# MYSQL使用者名稱
dbuser = root

# MYSQL密碼
dbpassword = 你的密碼

# MYSQL資料庫名稱
dbname = db_beego

# MYSQL表字首
dbprefix = tb_

二 構建登陸邏輯
我在考慮是先把所有的結構搭出來,還是一個一個去實現呢,然後我選擇了後者,因為這樣可以更快的做出效果提升興趣,所以我準備一上來就把登陸邏輯做個模版出來

a.首先我們需要將資料庫連線註冊到beego的orm中,這是各個操作都會用到的,在models檔案下新建base.go:

package models

import (
    "github.com/astaxie/beego/orm"
    "github.com/astaxie/beego"
)

func Init() {
    dbhost := beego.AppConfig.String("dbhost")
    dbport := beego.AppConfig.String("dbport")
    dbuser := beego.AppConfig.String("dbuser")
    dbpassword := beego.AppConfig.String("dbpassword")
    dbname := beego.AppConfig.String("dbname")
    if dbport == "" {
        dbport = "3306"
    }
    dsn := dbuser + ":" + dbpassword + "@tcp(" + dbhost + ":" + dbport + ")/" + dbname + "?charset=utf8&loc=Asia%2FShanghai"
    orm.RegisterDataBase("default", "mysql", dsn)
    orm.RegisterModel(new(User))
}

//返回帶字首的表名
func TableName(str string) string {
    return beego.AppConfig.String("dbprefix") + str
}

上面這段程式碼還是很容易看懂的,就是初始化資料庫連線,並且將User註冊到orm裡去

b.登入當然是跟使用者相關了,在models繼續新建user.go:

package models

import "time"

type User struct {
    Id         int
    Username   string
    Password   string
    Email      string
    LoginCount int
    LastTime   time.Time
    LastIp     string
    State      int8
    Created    time.Time
    Updated    time.Time
}

func (m *User) TableName() string {
    return TableName("user")
}

c.我們要注意不允許在未登入的情況下訪問我們的controller,所以需要判斷是否已經登入,在controller檔案下新建base.go:

package controllers

import (
    "github.com/astaxie/beego"
    "github.com/astaxie/beego/orm"
    "strings"
    "github.com/Echosong/beego_blog/models"
)

type baseController struct {
    beego.Controller
    o orm.Ormer
    controllerName string
    actionName     string
}

func (p *baseController) Prepare()  {
    controllerName, actionName := p.GetControllerAndAction()
    p.controllerName = strings.ToLower(controllerName[0 : len(controllerName)-10])
    p.actionName = strings.ToLower(actionName)
    p.o = orm.NewOrm();
    if strings.ToLower( p.controllerName) == "admin" && strings.ToLower(p.actionName)  !=  "login"{
        if p.GetSession("user") == nil{
            p.History("未登入","/admin/login")
            //p.Ctx.WriteString(p.controllerName +"==="+ p.actionName)
        }
    }

    //初始化前臺頁面相關元素
    if strings.ToLower( p.controllerName) == "blog"{

        p.Data["actionName"] = strings.ToLower(actionName)
        var result []*models.Config
        p.o.QueryTable(new(models.Config).TableName()).All(&result)
        configs := make(map[string]string)
        for _, v := range result {
            configs[v.Name] = v.Value
        }
        p.Data["config"] = configs
    }

}
//用來做跳轉的邏輯展示
func (p *baseController) History(msg string, url string) {
    if url == ""{
        p.Ctx.WriteString("<script>alert('"+msg+"');window.history.go(-1);</script>")
        p.StopRun()
    }else{
        p.Redirect(url,302)
    }
}


//獲取使用者IP地址
func (p *baseController) getClientIp() string {
    s := strings.Split(p.Ctx.Request.RemoteAddr, ":")
    return s[0]
}

Prepare()方法就是驗證使用者是否登入。

d.那麼我們現在來寫我們的登入controller,在controller資料夾下新建admin.go

package controllers

import (
    "github.com/liwd/blog/models"
    "strconv"
    "github.com/liwd/blog/util"
    "fmt"
    "strings"
    "time"
)
//繼承baseController
type AdminController struct {
    baseController
}

//後臺使用者登入
func (c *AdminController) Login() {
    if c.Ctx.Request.Method == "POST" {
        username := c.GetString("username")
        password := c.GetString("password")
        user := models.User{Username:username}
        c.o.Read(&user,"username")

        if user.Password == "" {
            c.History("賬號不存在","")
        }

        if util.Md5(password) != strings.Trim(user.Password, " ") {
            c.History("密碼錯誤", "")
        }
        user.LastIp = c.getClientIp()
        user.LoginCount = user.LoginCount +1
        if _, err := c.o.Update(&user); err != nil {
            c.History("登入異常", "")
        } else {
            c.History("登入成功", "/admin/main.html")
        }
        c.SetSession("user", user)
    }
    c.TplName = c.controllerName+"/login.html"
}

//主頁
func (c *AdminController) Main() {
    c.TplName = c.controllerName + "/main.tpl"
}

c.TplName相當於http.Handle(http.FileServer())是用來尋找html的。
這裡使用了md5對密碼加密,我們在util檔案下新建function.go:

package util

import (
    "crypto/md5"
    "crypto/rand"
    "encoding/base64"
    "fmt"
    "io"
    "net/url"
    "strings"
)

func Md5(str string) string {
    hash := md5.New()
    hash.Write([]byte(str))
    return fmt.Sprintf("%x", hash.Sum(nil))
}

func Rawurlencode(str string) string {
    return strings.Replace(url.QueryEscape(str), "+", "%20", -1)
}

//生成Guid字串
func UniqueId() string {
    b := make([]byte, 48)

    if _, err := io.ReadFull(rand.Reader, b); err != nil {
        return ""
    }
    return Md5(base64.URLEncoding.EncodeToString(b))
}

e.構建路由

因為Java裡面tomcat或者其它server已經幫我們構建好了路由便不需要我們自己構建了,而go自己已經封裝好了net/http模組,所以我們自己構建路由也很是方便,修改router.go:

package routers

import (
    "github.com/liwd/blog/controllers"
    "github.com/astaxie/beego"
)

func init() {
    bego.AutoRouter(&controllers.AdminController{})
}

這裡 bego.AutoRouter(&controllers.AdminController{})將AdminController的所有方法自動註冊為路由了,訪問/admin/login.html或者/admin/login都可以跳轉到login方法,是不是很簡單!

f.編寫登入頁面和登陸後的主頁面
首先新建static檔案,將我專案中static檔案下的所有資源複製到你的專案中,然後編寫登入頁面,在view資料夾中新建admin資料夾,在admin資料夾中新建login.html:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>後臺登入</title>
    <link rel="stylesheet" href="/static/plug/layui/css/layui.css">
    <style>

        .main {               
            margin: 0 auto;
            width: 400px;
            border: 1px solid;
            border-color: #eeeeee;
            border-radius: 5px;
            margin-top: 100px;
        }
    </style>

</head>
<body>
<script type="text/javascript" src="/static/plug/layui/layui.js"></script>
<div class="main layui-clear">
    <form action="/admin/login" method="post">
        <div class="fly-panel fly-panel-user" pad20>
            <div class="layui-tab layui-tab-brief">
                <ul class="layui-tab-title">
                    <li class="layui-this">歡迎登入後臺系統</li>
                </ul>
                <div class="layui-form layui-tab-content" id="LAY_ucm" style="padding-top: 20px; padding-left: 50px;  ">

                    <div class="layui-form layui-form-pane">

                        <div class="layui-form-item">
                            <label class="layui-form-label">使用者名稱</label>
                            <div class="layui-input-inline">
                                <input type="text" name="username" required lay-verify="username" 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="password"
                                       placeholder="密碼" autocomplete="off" class="layui-input">
                            </div>
                        </div>

                        <div class="layui-form-item" style="float: right; margin-right: 42px;">

                            <input type="checkbox" name="is_top" {{if .post.IsTop}} checked {{end}} value="1"
                                   title="記住密碼">

                        </div>

                        <div class="layui-form-item">
                            <button lay-submit class="layui-btn btn-submit" style="width: 300px; border-radius:3px"
                                    lay-submit=""
                                    lay-filter="sub">立即登入
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </form>
</div>

</body>
<script>
    //Demo
    layui.use('form', function () {
    });
</script>
</html>

這裡主要使用layui的模版,layui的學習門檻是極低的,關於layui的具體使用和特性可以去官網學習http://www.layui.com/doc/

同樣在admin資料夾下新建main.tpl:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>後臺管理系統</title>
    <link rel="stylesheet" href="/static/plug/layui/css/layui.css">
    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
</head>

<body>
<!-- 佈局容器 -->
<div class="layui-layout layui-layout-admin">
    <!-- 頭部 -->
    <div class="layui-header">
        <div class="layui-main">
            <!-- logo -->
            <a href="/" style="color: #c2c2c2; font-size: 18px; line-height: 60px;">後臺管理系統</a>
            <!-- 水平導航 -->
            <ul class="layui-nav" style="position: absolute; top: 0; right: 0; background: none;">
                <li class="layui-nav-item">
                    <a href="javascript:;">

                    </a>
                </li>
                <li class="layui-nav-item">
                    <a href="javascript:;">
                        admin,歡迎你!
                    </a>
                    <dl class="layui-nav-child">
                        <dd>
                            <a href="/admin/logout">
                                退出系統
                            </a>
                        </dd>
                    </dl>
                </li>
            </ul>
        </div>
    </div>

    <!--側邊欄 -->
    <div class="layui-side layui-bg-black">
        <div class="layui-side-scroll">
            <ul class="layui-nav layui-nav-tree " lay-filter="left-nav" style="border-radius: 0;">
            </ul>
        </div>
    </div>

    <!-- 主體 -->
    <div class="layui-body">
        <!-- 頂部切換卡 -->
        <div class="layui-tab layui-tab-brief" lay-filter="top-tab" lay-allowClose="true" style="margin: 0;">
            <ul class="layui-tab-title"></ul>
            <div class="layui-tab-content"></div>
        </div>
    </div>

    <!-- 底部 -->
    <div class="layui-footer" style="text-align: center; line-height: 44px;">
        Copyright ©無鞋
    </div>
</div>

<script src="/static/plug/layui/lay/lib/jquery.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="/static/plug/layui/layui.js"></script>
<script type="text/javascript">
    /**
     * 對layui進行全域性配置
     */
    layui.config({
        base: '/static/js/'
    });

    layui.use('form', function() {
        var $ = layui.jquery,
            form = layui.form();
    });

    /**
     * 初始化整個系統骨架
     */
    layui.use(['cms'], function() {
        var cms = layui.cms('left-nav', 'top-tab');
        cms.addNav([
            { id: 1, pid: 0, node: '<span style=" font-size: 16px"><i class="layui-icon">&#xe620;</i> 系統管理</span>', url: '#' },
            { id: 7, pid: 1, node: '&nbsp;&nbsp;&nbsp;系統設定', url: '/admin/config.html' },
            { id: 2, pid: 0, node: '<span style=" font-size: 16px"><i class="layui-icon">&#xe63c;</i> 內容管理</span>', url: '#' },
            { id: 3, pid: 2, node: '&nbsp;&nbsp;&nbsp;分類管理', url: '/admin/category.html' },
            { id: 5, pid: 2, node: '&nbsp;&nbsp;&nbsp;博文列表', url: '/admin/index.html' },
            { id: 6, pid: 2, node: '&nbsp;&nbsp;&nbsp;博文新增', url: '/admin/article.html' },
        ], 0, 'id', 'pid', 'node', 'url');
        cms.bind(60 + 41 + 20 + 44); //頭部高度 + 頂部切換卡標題高度 + 頂部切換卡內容padding + 底部高度
        cms.clickLI(1);

    });

    function addTab(title, src, id,closeId){
        if(closeId){
//                  debugger;
            closeTab(closeId);
        }
        layui.use(['cms'], function() {
            var cms = layui.cms('left-nav', 'top-tab');
            cms.addTab(title,src,id);
        });
    }

    function closeTab(id,refreshId){
        layui.use(['cms'], function() {
            var cms = layui.cms('left-nav', 'top-tab');
            cms.closeTab(id,refreshId);
        });
    }

</script>
</body>

</html>

進入blog目錄,執行bee run命令,訪問http://localhost:8088/admin/login
這裡寫圖片描述

輸入admin 密碼123456登入成功:

這裡寫圖片描述

先不管右邊主體的錯誤,後面我們會完善。

好了到這裡我們就搭建完了登入邏輯,這個流程是這樣的:
啟動專案後,初始化資料庫註冊orm的相關model,然後通過router.go來註冊路由,當我們訪問http://localhost:8088/admin/login會先到baseController的Prepare方法驗證是否已經登入(我們設定了session,如果已經登入可以直接訪問http://localhost:8088/admin/main.html進入後臺管理頁面,沒登陸成功或者session失效肯定是不行的),如果沒登入會進入login.html頁面,輸入帳號密碼通過自動繫結的路由自動跳轉到AdminController的Login方法,驗證帳號密碼的正確與否再去判斷邏輯

github地址:https://github.com/lightTrace/beego-blog

相關文章