ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用 beego 框架實現部落格增刪改:MVC 部落格系統完整實戰(build-web-application-with-golang 第 13.5 節)

用 beego 框架實現部落格增刪改:MVC 部落格系統完整實戰(build-web-application-with-golang 第 13.5 節) 文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本篇技術指南以開源電子書《build-web-application-with-golang》第 13.5 節為核心講解如何基於前幾節自研的 beego 框架快速搭建一個具備瀏覽、新增、修改、刪除完整能力的部落格系統。文中將完整繼承原章節的目錄結構、路由規則、資料庫設計、五個控制器、beedb 資料層與五個範本檔案並結合本倉庫第 13 章其他小節的框架源碼路由分發、控制器生命週期、範本渲染做縱深拆解。讀完本章你可以掌握 REST 風格 MVC 應用的完整搭建流程並理解 beego 框架從請求進來到頁面輸出的每一環。本節在整個框架章節中的位置第 13 章在 13.0.md 中規劃了「如何設計一個 Web 框架」的完整路線先講 MVC 專案規劃與資料流13.1 專案規劃再實現支援正則參數與 REST 方法分發的路由器13.2 自訂路由器設計接著設計公共控制器基底類別與自動渲染機制13.3 controller 設計並補上日誌分級與配置解析13.4 日誌和配置設計。本節 13.5 實現部落格的增刪改 是整個章節的「驗收工程」把前面設計的框架能力全部串起來落地成一個可執行的部落格站點。整個專案只有三個目錄——controllers、models、views外加一個入口檔案main.go卻完整覆蓋了 CRUD 的全部場景正好呼應 13.3 中「controller 是 MVC 中必不可少的一環而 Model 與 View 在部分場景可以省略」的設計哲學。部落格專案目錄結構原章節給出的部落格目錄如下. ├── controllers │ ├── delete.go │ ├── edit.go │ ├── index.go │ ├── new.go │ └── view.go ├── main.go ├── models │ └── model.go └── views ├── edit.tpl ├── index.tpl ├── layout.tpl ├── new.tpl └── view.tpl這套結構與 13.1 專案規劃 中設計的目錄藍圖一一對應main.go應用入口負責初始化框架、註冊路由、啟動監聽controllers/控制器入口五個檔案分別對應首頁、詳細、新增、編輯、刪除五種業務models/model.go資料庫處理模組封裝全部資料存取views/檢視範本目錄其中layout.tpl是全域性佈局其餘四個是具體頁面。可以看到 13.1 規劃中的conf、utils、static等目錄在這裡被有意省略了——部落格範例足夠簡單不需要獨立配置模組與靜態檔案目錄這正是「框架只是工具適合自己的才是最好的」思想的體現。部落格路由五條規則對應五大控制器部落格的主要路由規則如下位於入口檔案main.go中依據目錄結構與本節路由清單可以推斷//顯示部落格首頁 beego.Router(/, controllers.IndexController{}) //檢視部落格詳細資訊 beego.Router(/view/:id([0-9]), controllers.ViewController{}) //建立部落格博文 beego.Router(/new, controllers.NewController{}) //刪除博文 beego.Router(/delete/:id([0-9]), controllers.DeleteController{}) //編輯博文 beego.Router(/edit/:id([0-9]), controllers.EditController{})這五條規則涵蓋了部落格的全部 URL 空間背後正是 13.2 自訂路由器設計 中實現的兩大能力正則參數支援/view/:id([0-9])這種寫法解決了 Go 內建http.DefaultServeMux不支援/user/:uid泛型匹配的限制。路由註冊時會把:id替換成正則([0-9])並編譯請求到來時透過regexp.FindStringSubmatch取出子匹配作為參數存入Context.Params控制器內即可透過this.Ctx.Input.Params()[:id]讀取。REST 方法分發路由把請求轉發到 struct 而非函式13.2 的 ServeHTTP 實現 會依r.Method動態呼叫Init → Prepare → Get/Post/Put/Delete… → Render → Finish。因此/new這條路由同時服務 GET顯示表單與 POST提交表單兩種請求分別命中NewController的Get()與Post()方法。值得留意的是/delete/:id([0-9])與/edit/:id([0-9])都使用了[0-9]正則約束這保證了 URL 中的 id 必然是純數字從入口處就過濾掉了非法請求也是 13.4 中提到的「安全檢測」思想在路由層的體現。資料庫結構最小化部落格資料表資料庫設計最簡單的部落格資訊CREATE TABLE entries ( id INT AUTO_INCREMENT, title TEXT, content TEXT, created DATETIME, primary key (id) );這張entries表只有四個欄位id自增主鍵INT AUTO_INCREMENT由資料庫自動分配與 model 層Blog.Id的PK標記對應title標題TEXT型別content正文TEXT型別created建立/更新時間DATETIME由控制器在寫入時以time.Now()賦值。後續 model 層程式碼中資料庫連線字串是blog/astaxie/123456格式為資料庫名/使用者名稱/密碼因此需要預先建立名為blog的資料庫並授權astaxie使用者。本倉庫第 5 章提供了多個可對照的建表範例例如 ch.5.2 的 schema.sql、ch.5.4 的 schema.sql、ch.5.5 的 schema.sql均展示了 Go 應用配合 MySQL 建表與連線的標準姿勢。控制器層五個控制器實現 REST 增刪改查IndexController首頁列表type IndexController struct { beego.Controller } func (this *IndexController) Get() { this.Data[blogs] models.GetAll() this.Layout layout.tpl this.TplName index.tpl }IndexController只實現了Get()呼叫models.GetAll()取回全部博文存入this.Data[blogs]然後設定佈局範本layout.tpl與內容範本index.tpl。根據 13.3 controller 設計Get()執行完畢後若全域性AutoRendertrue框架會自動呼叫Render()把layout.tpl作為外殼、index.tpl的渲染結果嵌入{{.LayoutContent}}後輸出。ViewController檢視單篇詳細資訊type ViewController struct { beego.Controller } func (this *ViewController) Get() { id, _ : strconv.Atoi(this.Ctx.Input.Params()[:id]) this.Data[Post] models.GetBlog(id) this.Layout layout.tpl this.TplName view.tpl }ViewController從路由參數中取出:idthis.Ctx.Input.Params()[:id]對應 13.2 路由實現中存入Context.Params的正則子匹配。由於路由正則限定[0-9]此處strconv.Atoi的錯誤可以被安全忽略id, _ :。取到單篇博文後存入this.Data[Post]交由view.tpl渲染。NewController新增博文type NewController struct { beego.Controller } func (this *NewController) Get() { this.Layout layout.tpl this.TplName new.tpl } func (this *NewController) Post() { inputs : this.Input() var blog models.Blog blog.Title inputs.Get(title) blog.Content inputs.Get(content) blog.Created time.Now() models.SaveBlog(blog) this.Ctx.Redirect(302, /) }這是 REST 控制器「一對二」的典型範例同一條/new路由GET 請求渲染new.tpl表單頁POST 請求處理表單提交。Post()的流程是this.Input()取得請求輸入物件inputs.Get(title)與inputs.Get(content)讀取表單欄位組裝models.Blog其中Created由time.Now()填充Id留空——這對 beedb 的Save操作至關重要詳見 model 層一節呼叫models.SaveBlog(blog)寫入資料庫this.Ctx.Redirect(302, /)以 302 跳轉回首頁符合 13.3 提到的「沒有頁面輸出的 302 調整」場景——這種場景完全可以不寫 View。EditController編輯博文type EditController struct { beego.Controller } func (this *EditController) Get() { id, _ : strconv.Atoi(this.Ctx.Input.Params()[:id]) this.Data[Post] models.GetBlog(id) this.Layout layout.tpl this.TplName edit.tpl } func (this *EditController) Post() { inputs : this.Input() var blog models.Blog blog.Id, _ strconv.Atoi(inputs.Get(id)) blog.Title inputs.Get(title) blog.Content inputs.Get(content) blog.Created time.Now() models.SaveBlog(blog) this.Ctx.Redirect(302, /) }EditController與NewController結構高度對稱差別在於兩點Get()需要先依:id載入既有博文存入this.Data[Post]讓edit.tpl用{{.Post.Title}}等預填表單值Post()必須從表單隱藏欄位id讀取主鍵對應edit.tpl中的input typehidden nameid value{{.Post.Id}}這樣Save時 beedb 才能判斷是「更新既有記錄」而非「新增記錄」。DeleteController刪除博文type DeleteController struct { beego.Controller } func (this *DeleteController) Get() { id, _ : strconv.Atoi(this.Ctx.Input.Params()[:id]) blog : models.GetBlog(id) this.Data[Post] blog models.DelBlog(blog) this.Ctx.Redirect(302, /) }DeleteController是「副作用型 GET」的簡化寫法先依 id 查出Blog物件再呼叫models.DelBlog(blog)刪除最後 302 回首頁。刪除動作直接掛在 GET 請求上頁面上的Delete連結即為普通a href這在真實專案中通常會換成 DELETE 方法或至少加上二次確認但作為框架教學範例它用最少的程式碼演示了 beedb 的刪除介面。Model 層透過 beedb 封裝資料存取package models import ( database/sql github.com/astaxie/beedb _ github.com/ziutek/mymysql/godrv time ) type Blog struct { Id int PK Title string Content string Created time.Time } func GetLink() beedb.Model { db, err : sql.Open(mymysql, blog/astaxie/123456) if err ! nil { panic(err) } orm : beedb.New(db) return orm } func GetAll() (blogs []Blog) { db : GetLink() db.FindAll(blogs) return } func GetBlog(id int) (blog Blog) { db : GetLink() db.Where(id?, id).Find(blog) return } func SaveBlog(blog Blog) (bg Blog) { db : GetLink() db.Save(blog) return bg } func DelBlog(blog Blog) { db : GetLink() db.Delete(blog) return }Model 層的全部邏輯就是「一個結構體 五個函式」Blog結構體Id int帶有PK標記beedb 依此識別主鍵欄位並對映到資料表的id列Created time.Time對映到DATETIME型別的created列。GetLink()每次操作都建立新的連線——sql.Open(mymysql, blog/astaxie/123456)開啟 MySQL 連線DSN 格式為資料庫/使用者/密碼再透過beedb.New(db)包裝成 beedb 的 ORM 模型。需要注意的是連線資訊目前是硬編碼的13.4 日誌和配置設計 介紹的beego.AppConfig.String(url)讀取 ini 配置的機制正是把這類「伺服器相關配置」抽離到app.conf的標準做法。GetAll()db.FindAll(blogs)一次查出全部記錄並填充[]Blog切片供首頁列表使用。GetBlog(id)db.Where(id?, id).Find(blog)以參數化查詢定位單篇博文?佔位符避免 SQL 注入。SaveBlog(blog)db.Save(blog)是 beedb 的「有主鍵則更新、無主鍵則插入」操作因此NewController.Post()中Id為零值時會新增EditController.Post()中Id來自隱藏欄位時會更新。另外從程式碼可以看出SaveBlog宣告了回傳值bg但始終未賦值實際回傳的是零值Blog而兩個呼叫端目前都沒有使用其回傳值因此不影響功能——這是一處值得讀者注意的小瑕疵。DelBlog(blog)db.Delete(blog)依主鍵刪除記錄。依賴方面model 層引入了github.com/astaxie/beedb輕量 ORM與github.com/ziutek/mymysql/godrvMySQL 驅動透過空白匯入註冊mymysql驅動名執行前需要先go get這兩個依賴。View 層五個範本完成頁面渲染layout.tpl全域性佈局html head titleMy Blog/title style #menu { width: 200px; float: right; } /style /head body ul idmenu lia href/Home/a/li lia href/newNew Post/a/li /ul {{.LayoutContent}} /body /htmllayout.tpl是站點外殼右側固定導覽列Home 與 New Post正文位置由{{.LayoutContent}}佔位。這個佔位符正是 13.3 controller 設計 中Render()的協作成果——當控制器設定了this.Layout框架會用template.ParseFiles同時解析佈局與內容範本再以ExecuteTemplate(w, c.TplNames, c.Data)把子範本渲染結果填入{{.LayoutContent}}。index.tpl首頁列表h1Blog posts/h1 ul {{range .blogs}} li a href/view/{{.Id}}{{.Title}}/a from {{.Created}} a href/edit/{{.Id}}Edit/a a href/delete/{{.Id}}Delete/a /li {{end}} /ulindex.tpl使用 Go 範本引擎的{{range .blogs}}遍歷控制器注入的博文切片每篇博文輸出標題連結跳轉/view/{{.Id}}、建立時間以及 Edit、Delete 兩個操作連結分別指向/edit/{{.Id}}與/delete/{{.Id}}。這三個連結與前文五條路由完全對應構成了完整的操作入口。view.tpl單篇詳細h1{{.Post.Title}}/h1 {{.Post.Created}}br/ {{.Post.Content}}view.tpl直接以點路徑取用ViewController注入的Post物件欄位展示標題、時間與正文。new.tpl新增表單h1New Blog Post/h1 form action methodpost 標題:input typetext nametitlebr 內容textarea namecontent colspan3 rowspan10/textarea input typesubmit /formnew.tpl是新增頁表單action表示提交到當前 URL即/newmethodpost觸發NewController.Post()表單欄位名title、content與控制器中inputs.Get(title)、inputs.Get(content)一一對應。edit.tpl編輯表單h1Edit {{.Post.Title}}/h1 h1New Blog Post/h1 form action methodpost 標題:input typetext nametitle value{{.Post.Title}}br 內容textarea namecontent colspan3 rowspan10{{.Post.Content}}/textarea input typehidden nameid value{{.Post.Id}} input typesubmit /formedit.tpl與new.tpl的關鍵差異在於三處預填值標題輸入框的value{{.Post.Title}}、正文 textarea 內的{{.Post.Content}}以及最關鍵的input typehidden nameid value{{.Post.Id}}隱藏主鍵欄位——它確保EditController.Post()能從inputs.Get(id)恢復記錄主鍵進而讓 beedb 執行更新而非插入。另外從範本程式碼可以看到頁面標題出現了Edit {{.Post.Title}}與New Blog Post兩行h1後者應是範本複用時的殘留不影響功能但屬於可以清理的小瑕疵。請求生命週期一次完整的新增與編輯流程把控制器、路由與資料層串起來一次「新增博文」請求的完整路徑是瀏覽器 GET/new→ 路由器比對/new規則 → 以反射建立NewController實例執行Init → Prepare → Get()見 13.2 的ServeHTTP分發邏輯Get()設定Layout layout.tpl與TplName new.tplRender()將表單頁嵌入佈局後輸出使用者填表並 POST 到/new→ 路由器依 method 改為執行Post()Post()讀取表單、組裝Blog、呼叫models.SaveBlog寫庫再Redirect(302, /)瀏覽器跟隨跳轉 GET/→IndexController.Get()呼叫GetAll()查出最新列表並渲染新博文出現在首頁。「編輯」流程同理只是 GET 階段多了「依 id 查詢並預填」POST 階段多了「讀取隱藏 id 走更新語義」。這與 13.1 專案規劃 中描述的框架資料流入口初始化 → 路由分發 → 控制器裝載 model → 輸出 view完全吻合本節部落格就是該流程圖的一次具體落地。執行前置條件與注意事項在執行這個部落格範例前需要準備GOPATH 與專案目錄依 13.1 專案規劃設定GOPATH並保證其下有src/pkg/bin三個目錄部落格原始碼放在$GOPATH/src下依賴庫go get github.com/astaxie/beedb與go get github.com/ziutek/mymysql前者是 ORM後者提供mymysql驅動資料庫依blog/astaxie/123456建立名為blog的資料庫、使用者astaxie、密碼123456並執行本節的CREATE TABLE entries建表語句範本命名控制器中Layout、TplName均為相對於views目錄的檔名13.3 提到框架預設範本擴展名TplExt tpl本節的.tpl檔案與之匹配自動渲染確認AutoRender為開啟狀態Get()/Post()執行完畢後框架才會自動呼叫Render()輸出頁面13.2、13.3 中的ServeHTTP與Render()實現。延伸閱讀框架總體規劃與資料流13.0 如何設計一個 Web 框架、13.1 專案規劃路由的正則參數與 REST 分發源碼13.2 自訂路由器設計控制器基底類別與渲染機制13.3 controller 設計日誌分級與 ini 配置解析13.4 日誌和配置設計本章總結與後續路線13.6 小結全書目錄preface.md相關範例程式碼第 5 章資料庫範例、範本範例 login.gtpl赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐KeystoneJS测试体系搭建单元测试与Nightwatch E2E自动化测试完整教程KeystoneJS测试体系搭建单元测试与Nightwatch E2E自动化测试完整教程 KeystoneJS 是一款基于 Node.js 的内容管理系统C文档教程IoT-For-Beginners 实战使用 MQTT 发布 DHT11 温度遥测数据为 GDD 积温计算铺路IoT For Beginners 实战使用 MQTT 发布 DHT11 温度遥测数据为 GDD 积温计算铺路 本指南聚焦于 Azure IoT For B文档教程Go 處理 JSON 全攻略encoding/json 的解析、產生與 struct tag 實戰build-web-application-with-golang 第 7.2 節Go 處理 JSON 全攻略encoding/json 的解析、產生與 struct tag 實戰build web application with go文档教程上一篇告别等待LanceDB异步API让你的AI应用并发能力飙升10倍下一篇Kaniko构建镜像的元数据暴露Prometheus指标深度实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表