Web 開發之 Beego
使用 go get 安裝 bee 工具與 beego
使用 bee 工具初始化 Beego 項目
- 在
$GOPATH/src目錄下執行bee create myapp
使用 bee 工具熱編譯 Beego 項目
- 在
$GOPATH/src/myapp目錄下執行bee start myapp
// hello world
package main
import (
"github.com/astaxie/beego"
)
type HomeController struct {
beego.Controller
}
// 注意Get首字母大寫。要不會報method not allow
func (this *HomeController) Get() {
this.Ctx.WriteString("Hello World")
}
func main() {
beego.Router("/",&HomeController)
beego.Run()
}
go http
三個包 io/log/net/http
package main
import (
"io"
"log"
"net/http"
)
func main() {
http.HandleFunc("/",sayHello)
err:=http.ListenAndServe(":8080",nil)
if err!=nil {
log.Fatal(err)
}
}
func sayHello(w http.ResponseWriter,r *http.Request) {
io.WriteString(w,"Hello world,this is is version 1.")
}
為ListenAdnServe第二個參數添加 handler
package main
import (
"io"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.Handle("/", &myHandler{})
mux.HandleFunc("/hello",SayHelloV2) //處理func
err := http.ListenAndServe("0.0.0.0:8080", mux)
if err!=nil {
log.Fatal(err)
}
}
type myHandler struct{}
func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "URL:"+r.URL.String())
}
// 同樣mux也可以HandleFunc
func SayHelloV2(w http.ResponseWriter,r *http.Request) {
io.WriteString(w,"Hello world, this is version No.2.")
}
更底層的寫處理 HttpServer
package main
import (
"io"
"log"
"net/http"
"time"
)
//手動處理路由,在handler中只有一個ServerHTTP
var mx map[string]func(w http.ResponseWriter,r *http.Request)
func main() {
server:=http.Server{
Addr:":8080",
Handler: &myHandler1{},
ReadHeaderTimeout:5*time.Second,
}
//路由註冊
mx = make(map[string]func(w http.ResponseWriter,r *http.Request))
mx["/hello"] =sayHello
mx["/saybye"] = sayBye
err:=server.ListenAndServe()
if err!=nil {
log.Fatal(err)
}
}
type myHandler1 struct{}
func (*myHandler1) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//io.WriteString(w, "URL:"+r.URL.String())
//這裡來做路由轉發
if h,ok:=mx[r.URL.String()];ok{
h(w,r)
return
}
io.WriteString(w, "妹找到-->URL:"+r.URL.String())
}
func sayHello (w http.ResponseWriter, r *http.Request) {
io.WriteString(w,"Hello world, this is version no 3")
}
func sayBye(w http.ResponseWriter, r *http.Request) {
io.WriteString(w,"Bye bye, this is version no 3")
}
對於 mux 的擴展靜態服務
func main() {
mux := http.NewServeMux()
mux.Handle("/", &myHandler{})
mux.HandleFunc("/hello", SayHelloV2)
//創建靜態服務
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
mux.Handle("/static/",
http.StripPrefix("/static/", http.FileServer(http.Dir(wd))))
err = http.ListenAndServe("0.0.0.0:8080", mux)
if err != nil {
log.Fatal(err)
}
}
bee 命令的使用
- 新建一個 web 項目
bee new myproject - 通過 api 命令可是用來創建 api 應用
bee api apiproject,其目錄結構中我們可以看出少了 static 和 views 目錄,多了一個 test 模塊,用來做單元測試 bee run命令是監控 beego 的項目,通過 fsnotify 監控文件系統。但是注意該命令必須在$GOPATH/src/appname下執行bee packpack 目錄用來發佈應用的時候打包,會把項目打包成 zip 包,這樣我們部署的時候直接把打包之後的項目上傳,解壓就可以部署了bee version查看 bee beego 和 go 的版本bee generate命令,用來自動化生成代碼的,包含了從數據庫一鍵生成 model,還包含了 scaffold
beego 配置管理
conf/app.conf 怎麼獲取呢?
package controllers
import (
"strconv"
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
// c.Data["Website"] = "beego.me"
// c.Data["Email"] = "astaxie@gmail.com"
// c.TplName = "index.tpl"
c.Ctx.WriteString("appName:"+beego.AppConfig.String("appname")+
"\nhttpport"+beego.AppConfig.String("httpport")+
"\nrunmode"+beego.Appconfig.String("runmode"))
// 系統默認參數方式 貌似不帶這種方式了
/* hp:=strcovItoa(beego.HttpPort)
c.Ctx.WriteString("appName:"+beego.AppName+
"\nhttpport"+hp+
"\nrunmode"+beego.RunMode) */
// 日誌 及日誌級別
/*
LevelEmergency = iota // 緊急級別
LevelAlert // 報警級別
LevelCritical // 嚴重錯誤級別
LevelError // 錯誤級別
LevelWarning // 警告級別
LevelNotice // 注意級別
LevelInformational // 報告級別
LevelDebug // 除錯級別
*/
log:=logs.NewLogger(10000)
log.SetLevel(logs.LevelDebug)
}
模板
package controllers
import (
"strconv"
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
c.Data["name"] = "name"
// 模板中的條件判斷
c.Data["TrueCond"] = true
c.Data["FalseCond"] = false
// 打印一個結構
type u struct{
Name string
age int
Sex string
}
user:=&u{
Name:"Joe",
Age:20,
Sex:"Male"
}
c.Data["User"] = user
// 循環輸出
nums:=[]int{1,2,3,4,5,6,7,8,9,0}
c.Data["Nums"] = nums
// 給模板變量賦值
c.Data["TplVar"] = "hey guys"
// 內置模板函數的功能
c.Data["Html"] = "<div>Hello Beego</div>"
c.Data["Pipe"] = "<div>Hello Beego</div>"
}
<!-- tpl -->
<div>
<!-- 條件判斷 判斷值只能是True 或 false不能用表達式 -->
{{if .TrueCond}} true condition content {{end}}
</div>
<div>
<!-- 條件判斷 判斷值只能是True 或 false不能用表達式 -->
{{if .FalseCond}} {{else}} false condition content {{end}}
</div>
<div>
<!-- 結構顯示 -->
{{.User.Name}} {{.User.Age}} {{.User.Sex}}
<!-- 嵌套輸出 -->
{{with .User}} {{.Name}} {{.Age}} {{.Sex}} {{end}}
</div>
<div>
<!-- 單數組 只輸入一個.就可以了 -->
{{range .Nums}} {{.}} {{end}}
<!-- 如果是對象的話,因為兼顧了with功能,所以只寫對象屬性就可以了`.屬性名` -->
</div>
<div>
<!-- 模板變量使用$定義 -->
{{$tplVar = .TplVar}} ==> {{$tplVar}}
</div>
<div>
{{.html}}
<!-- 模板函數 -->
{{str2Html .html}}
</div>
<!-- 編碼 htmlquote函數-->
{{.Pipe | htmlquote}}
定義模板嵌套
{{define "test"}} 定義的模板塊 {{end}}
<html>
<head></head>
<body>
{{template "test"}}
</body>
</html>
實戰課 models 的建立
在 models 目錄下創建 models.go 文件(別搞混了,和我們之前的那個 xorm 不是一個哈)
// controllers/default.go
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (c *MainController) Get() {
c.TplName = "home.html"
}
// models/models.go 注意orm中後面是帶雙引號
type Category struct {
Id int64
Title string
Created time.Time `orm:"index;auto_now_add;type(datetime)"`
Views int64 `orm:"index"`
TopicTime time.Time `orm:"index;auto_now_add;type(datetime)"`
TopicCount int64
TopicLastUserId int64
}
type Topic struct {
Id int64
Uid int64
Title string
Content string `orm:size(5000)`
Attachment string
Created time.Time `orm:"index;auto_now_add;type(datetime)"`
Updated time.Time `orm:"index;auto_now_add;type(datetime)"`
Views int64 `orm:"index"`
Author string
ReplyTime time.Time
ReplyCount int64
ReplyLastUserId int64
}
func RegisterDB() {
if !com.IsExist(_DB_NAME) {
os.MkdirAll(path.Dir(_DB_NAME), os.ModePerm)
os.Create(_DB_NAME)
}
orm.RegisterModel(new(Category), new(Topic))
orm.RegisterDriver(_SQLITE3_DERIVER, orm.DRSqlite)
// 強制要求必須有一個數據庫叫default 不管你有幾個數據庫 最大連接數10
orm.RegisterDataBase("default", _SQLITE3_DERIVER, _DB_NAME, 10)
}
// main.go
package main
import (
"beeapp/controllers"
"beeapp/models"
_ "beeapp/routers"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
func init() {
models.RegisterDB()
}
func main() {
orm.Debug = true //調度模式
//自動建表
orm.RunSyncdb("default",false,true)
beego.Router("/",&controllers.MainController{})
beego.Run()
}
登錄及分類管理
拆分模板,將上例中的模板內容拆分成不同塊以備後續開發復用使用。如將網頁頭問提取出來命名為T_header.tpl
{{define "header"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="shortcut icon" href="/static/img/favicon.ico" />
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
<title>首頁</title>
</head>
<body style="padding-top: 70px">
{{end}}
</body>
</html>
在 home.html 模板中刪除對應的內容,並改為 beego 模板引入語法;定義模板要用{{define "header"}} {{end}}
注意最後面的那個
.,如果在模板中使用使用變量則需要這個點,如果是純靜態資源可以省略
{{template "header" .}}
<!-- 模板輸出變量{{.變量名}} -->
這裡使用 glide 來做包管理工具,也會碰到 go get golang.org/x/不能下載問題,可以使用添加映像來管理;這裡只添加了 tools net sys
~~坑已經狠狠的踩過了,千萬不要用 glide~~ 還有個坑就是 terminal 翻牆
glide mirror set https://golang.org/x/mobile https://github.com/golang/mobile --vcs git
glide mirror set https://golang.org/x/crypto https://github.com/golang/crypto --vcs git
glide mirror set https://golang.org/x/net https://github.com/golang/net --vcs git
glide mirror set https://golang.org/x/tools https://github.com/golang/tools --vcs git
glide mirror set https://golang.org/x/text https://github.com/golang/text --vcs git
glide mirror set https://golang.org/x/image https://github.com/golang/image --vcs git
glide mirror set https://golang.org/x/sys https://github.com/golang/sys --vcs git
別改一個標題請使用 dep
github,遇到 golang.org 下載不了問題看這裡 。 基本上就是解決 terminal 下科學上網問題
cate 列表業務
<!-- category模板 -->
{{template "header" .}} {{template "navbar" .}}
<div class="container">
<form class="form-inline" action="/cate" method="get">
<div class="form-group">
<label for="cate_name">類別名稱</label>
<input
type="text"
style="width: 260px"
class="form-control"
id="cate_name"
name="catename"
placeholder="Please enter category name"
/>
<input type="hidden" name="op" value="add" />
</div>
<button type="submit" class="btn btn-default">添加</button>
</form>
<table class="table table-striped">
<thead>
<th>#</th>
<th>名稱</th>
<th>文章數</th>
<th>操作</th>
</thead>
<tbody>
{{range .Categories}}
<td>{{.Id}}</td>
<td>{{.Title}}</td>
<td>{{.TopicCount}}</td>
<td>
<a href="/cate?op=del&id={{.Id}}">刪除</a>
</td>
{{end}}
</tbody>
</table>
</div>
{{template "footer" .}}
寫業務
// CategoryController
package controllers
import (
"beeapp/models"
"github.com/astaxie/beego"
)
type CategoryController struct {
beego.Controller
}
func (c *CategoryController) Get() {
op := c.Input().Get("op")
// 添加 刪除 顯示列表
switch op {
case "add":
name := c.Input().Get("catename")
if len(name) == 0 {
break
}
err := models.AddCategory(name)
if err != nil {
beego.Error(err)
}
//添加成功 重定向cate
c.Redirect("/cate", 301)
// 記得return
return
case "del":
id := c.Input().Get("id")
if len(id) == 0 {
break
}
err := models.DeleteCategory(id)
if err != nil {
beego.Error(err)
}
c.Redirect("/cate", 301)
return
}
c.Data["Title"] = "分類"
c.Data["IsCategory"] = true
c.TplName = "category.html"
var err error
c.Data["Categories"], err = models.GetAllCategories()
if err != nil {
beego.Error(err)
}
}
// models中添加如下方法
// 添加分類
func AddCategory(name string) error {
o := orm.NewOrm()
cate := &Category{Title: name}
qs := o.QueryTable("category")
err := qs.Filter("title", name).One(cate)
if err == nil {
//err為空說明找到了
return err
}
// 不存在 要做一個insert
_, err = o.Insert(cate)
if err != nil {
return err
}
return nil
}
func GetAllCategories() ([]*Category, error) {
o := orm.NewOrm()
cates := make([]*Category, 0)
qs := o.QueryTable("category")
//注意下面的取址符號
_, err := qs.All(&cates)
return cates, err
}
func DeleteCategory(id string) error {
cid, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return err
}
o := orm.NewOrm()
cate := &Category{Id: cid}
_, err = o.Delete(cate)
return err
}
simple http coookie
package main
import (
"io"
"strings"
"net/http"
)
func main() {
http.HandleFunc("/",Cookie)
http.HandleFunc("/2",Cookie2)
http.ListenAndServe(":9090",nil)
}
func Cookie(w http.ResponseWriter, r *http.Request) {
//第一種設置方法
ck := &http.Cookie{
Name: "myCookie",
Value: "hello",
Path: "/",
Domain: "localhost",
MaxAge: 120,
}
http.SetCookie(w, ck)
ck2, err := r.Cookie("myCookie")
if err != nil {
io.WriteString(w, err.Error())
return
}
io.WriteString(w, ck2.Value)
}
func Cookie2(w http.ResponseWriter, r *http.Request) {
//第二種設置方法
ck := &http.Cookie{
Name: "myCookie",
Value: strings.Replace("hello World"," ","%20",-1), //空格在cookie中是非法字符,需要替換處理,將其替換成非空格字符,注意cookie中的非法字符
Path: "/",
Domain: "localhost",
MaxAge: 120,
}
w.Header().Set("Set-Cookie",ck.String())
ck2, err := r.Cookie("myCookie")
if err != nil {
io.WriteString(w, err.Error())
return
}
io.WriteString(w, ck2.Value)
}
主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/6717