重構MVC
- 1 Article 模型
- 1.1 首先創建 Article 模型文件
- 1.2 接下來創建獲取文章的方法
- 1.3 新增 types.StringToUint64()函數
- 1.4 修改控制器的調用
- 1.5 重構 route 包
- 1.6 通過 SetRoute 來傳參對象變量
- 1.7 新增方法:
- 1.8 控制器將 Int64ToString 改為 Uint64ToString
- 1.9 模板里修改 Int64ToString 為 Uint64ToString:
- 1.10 修改logger:
- 1.11 修改articles_controller.go的判斷:
- 2 重構文章列表
- 2.1 移動articles.index路由
- 2.2 添加Index控制器方法
- 2.3 獲取文章列表 GetAll
- 2.4 調試程序
- 2.5 代碼版本標記
1 Article 模型
解決undefined: getArticleByID 的報錯
config := mysql.New(mysql.Config{DSN: "root:secret@tcp(127.0.0.1:3306)/goblog?charset=utf8&parseTime=True&loc=Local",})
在提供的代碼中,用戶名是"root",密碼是"secret"。這段代碼中的DSN(Data Source Name)參數指定了數據庫連接的信息,其中"root"是用戶名,“secret"是密碼。所以,根據這段代碼,用戶是"root”,密碼是"secret"。
1.1 首先創建 Article 模型文件
app/models/article/article.go
// Package article 應用的文章模型
package article// Article 文章模型
type Article struct {ID uint64 Title stringBody string
}
1.2 接下來創建獲取文章的方法
app/models/article/crud.go
package articleimport ("goblog/pkg/model""goblog/pkg/types"
)// Get 通過 ID 獲取文章
func Get(idstr string) (Article, error) {var article Articleid := types.StringToUint64(idstr)if err := model.DB.First(&article, id).Error; err != nil {return article, err}return article, nil
}
First() 是 gorm.DB 提供的用以從結果集中獲取第一條數據的查詢方法,.Error 是 GORM 的錯誤處理機制。與常見的 Go 代碼不同,因 GORM 提供的是鏈式 API,如果遇到任何錯誤,GORM 會設置 *gorm.DB 的 Error 字段。
1.3 新增 types.StringToUint64()函數
pkg/types/converter.go
.
.
.// StringToUint64 將字符串轉換為 uint64
func StringToUint64(str string) uint64 {i, err := strconv.ParseUint(str, 10, 64)if err != nil {logger.LogError(err)}return i
}
1.4 修改控制器的調用
app/http/controllers/articles_controller.go
.
.
.// Show 文章詳情頁面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {...// 2. 讀取對應的文章數據article, err := article.Get(id)...
}
1.5 重構 route 包
pkg/route/router.go
// Package route 路由相關
package routeimport ("goblog/pkg/logger""net/http""github.com/gorilla/mux"
)var route *mux.Router// SetRoute 設置路由實例,以供 Name2URL 等函數使用
func SetRoute(r *mux.Router) {route = r
}// Name2URL 通過路由名稱來獲取 URL
func Name2URL(routeName string, pairs ...string) string {url, err := route.Get(routeName).URL(pairs...)if err != nil {logger.LogError(err)return ""}return url.String()
}
.
.
.
1.6 通過 SetRoute 來傳參對象變量
bootstrap/route.go
.
.
.
// SetupRoute 路由初始化
func SetupRoute() *mux.Router {router := mux.NewRouter()routes.RegisterWebRoutes(router)route.SetRoute(router)return router
}
1.7 新增方法:
pkg/types/converter.go
.
.
.// Uint64ToString 將 uint64 轉換為 string
func Uint64ToString(num uint64) string {return strconv.FormatUint(num, 10)
}
1.8 控制器將 Int64ToString 改為 Uint64ToString
app/http/controllers/articles_controller.go
.
.
.
// Show 文章詳情頁面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {...} else {// 4. 讀取成功,顯示文章tmpl, err := template.New("show.gohtml").Funcs(template.FuncMap{"RouteName2URL": route.Name2URL,"Uint64ToString": types.Uint64ToString,}).
1.9 模板里修改 Int64ToString 為 Uint64ToString:
resources/views/articles/show.gohtml
{{/* 構建刪除按鈕 */}}{{ $idString := Uint64ToString .ID }}<form action="{{ RouteName2URL "articles.delete" "id" $idString }}" method="post"><button type="submit" onclick="return confirm('刪除動作不可逆,請確定是否繼續')">刪除</button></form>
瀏覽 localhost:3000/articles/3 ,顯示:
訪問localhost:3000/articles/1000,顯示拒絕訪問
1.10 修改logger:
pkg/logger/logger.go
// Package logger 日志相關
package loggerimport "log"// LogError 當存在錯誤時記錄日志
func LogError(err error) {if err != nil {log.Println(err)}
}
1.11 修改articles_controller.go的判斷:
GORM 有單獨的錯誤類型 —— gorm.ErrRecordNotFound
app/http/controllers/articles_controller.go
.
.
.// Show 文章詳情頁面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {...// 3. 如果出現錯誤if err != nil {if err == gorm.ErrRecordNotFound { // <---- 這一行// 3.1 數據未找到...} else {// 3.2 數據庫錯誤...}} else {// 4. 讀取成功,顯示文章...}
}
再次訪問localhost:3000/articles/521
2 重構文章列表
2.1 移動articles.index路由
把 main.go 里的articles.index路由剪切到 web.go 中,并簡單修改
routes/web.go
.
.
.
func RegisterWebRoutes(r *mux.Router) {...r.HandleFunc("/articles", ac.Index).Methods("GET").Name("articles.index")
}
2.2 添加Index控制器方法
將 main.go 中 articlesIndexHandler 函數剪切并修改名稱到控制器中:
app/http/controllers/articles_controller.go
// Index 文章列表頁
func (*ArticlesController) Index(w http.ResponseWriter, r *http.Request) {// 1. 獲取結果集articles, err := article.GetAll()if err != nil {// 數據庫錯誤logger.LogError(err)w.WriteHeader(http.StatusInternalServerError)fmt.Fprint(w, "500 服務器內部錯誤")} else {// 2. 加載模板tmpl, err := template.ParseFiles("resources/views/articles/index.gohtml")logger.LogError(err)// 3. 渲染模板,將所有文章的數據傳輸進去err = tmpl.Execute(w, articles)logger.LogError(err)}
}
2.3 獲取文章列表 GetAll
獲取文章列表封裝到模型的 GetAll 方法里:
app/models/article/crud.go
// GetAll 獲取全部文章
func GetAll() ([]Article, error) {var articles []Articleif err := model.DB.Find(&articles).Error; err != nil {return articles, err}return articles, nil
}
上面兩個步驟的代碼的功能與下面代碼一樣:
// 1. 執行查詢語句,返回一個結果集
rows, err := db.Query("SELECT * from articles")
logger.LogError(err)
defer rows.Close()var articles []Article
//2. 循環讀取結果
for rows.Next() {var article Article// 2.1 掃碼每一行的結果并賦值到一個 article 對象中err := rows.Scan(&article.ID, &article.Title, &article.Body)logger.LogError(err)// 2.2 將 article 追加到 articles 的這個數組中articles = append(articles, article)
}
// 2.3 檢測循環時是否發生錯誤
err = rows.Err()
logger.LogError(err)
GORM 的優勢之一:不需要時刻記住關閉連接
訪問 localhost:3000/articles :
沒有顯示數據
2.4 調試程序
GORM 提供了一個調試功能,允許在命令行里查看請求的 SQL 信息,config里面設置:
pkg/model/model.go
// Package model 應用模型數據層
package modelimport ("goblog/pkg/logger""gorm.io/gorm"gormlogger "gorm.io/gorm/logger"// GORM 的 MSYQL 數據庫驅動導入"gorm.io/driver/mysql"
)// DB gorm.DB 對象
var DB *gorm.DB// ConnectDB 初始化模型
func ConnectDB() *gorm.DB {var err errorconfig := mysql.New(mysql.Config{DSN: "root:secret@tcp(127.0.0.1:3306)/goblog?charset=utf8&parseTime=True&loc=Local",})// 準備數據庫連接池DB, err = gorm.Open(config, &gorm.Config{Logger: gormlogger.Default.LogMode(gormlogger.Info),})logger.LogError(err)return DB
}
頂部的 import 語句,導入 gorm/logger 時,因 goblog/pkg/logger 名稱沖突,故為其指定名稱:
gormlogger “gorm.io/gorm/logger”
刷新 localhost:3000/articles 頁面
[rows:3] 意味著從數據庫了成功取出了三條數據。
試著在控制器里打印一下 articles 變量:
app/http/controllers/articles_controller.go
.
.
.
// Index 文章列表頁
func (*ArticlesController) Index(w http.ResponseWriter, r *http.Request) {// 1. 獲取結果集articles, err := article.GetAll()fmt.Println("文章數據", articles)...
}
刷新 localhost:3000/articles 頁面,觀察命令行:
經調試,數據沒問題,模板index.gohtml的問題
resources/views/articles/index.gohtml查看模板
<!DOCTYPE html>
<html lang="en">
<head><title>所有文章 —— 我的技術博客</title><style type="text/css">.error {color: red;}</style>
</head>
<body><h1>所有文章</h1><ul>{{ range $key, $article := . }}<li><a href="{{ $article.Link }}"><strong>{{ $article.ID }}</strong>: {{ $article.Title }}</a></li>{{ end }}</ul>
</body>
</html>
$article.Link 這是一個對象方法,還未創建,將 main 里的對應方法移動到模型中,并簡單修改:
app/models/article/article.go
.
.
.
// Link 方法用來生成文章鏈接
func (article Article) Link() string {return route.Name2URL("articles.show", "id", strconv.FormatUint(article.ID, 10))
}
刷新 localhost:3000/articles 頁面
問題解決,清理剛才調試的內容:
- 刪除 fmt.Println(“文章數據”, articles)
- 設置日志級別為Warn即可,pkg/model/model.go 中設置為Logger: gormlogger.Default.LogMode(gormlogger.Warn)
- 刪除main.go中的 articlesIndexHandler 和 Link 這兩個函數
2.5 代碼版本標記
git add .
git commit -m "重構文章列表頁面"
git push //注釋,push到遠程github上