Gin博客项目-项目架构
规范包及目录
|-controller
|-dao
|-router
|-model
|-assets
|-templates
初始化项目
go mod init blog
Gin博客项目-集成gorm
下载包
go get -u gorm.io/gormgo get -u gorm.io/driver/mysql
创建模型
package models
import "github.com/jinzhu/gorm"
type User struct {
gorm.Model
Username string `json:"username"`
Password string `json:"passowrd"`
}
创建Dao
package dao
import (
"log"
"pro04/models"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Manager interface {
AddUser(user *models.User)
}
type manager struct {
db *gorm.DB
}
var Mgr Manager
func init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to init db:", err)
}
Mgr = &manager{db: db}
db.AutoMigrate(&models.User{})
}
func (mgr *manager) AddUser(user *models.User) {
mgr.db.Create(user)
}
测试
user := models.User{
Username: username,
Password: password,
}
dao.Mgr.AddUser(&user)
Gin博客项目-集成Bootstrap创建用户表单
下载bootstrap
https://getbootstrap.com/
添加js和css到assets目录下面
创建添加用户html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>用户管理</title>
</head>
<body>
<div class="container">
<div class="row mt-3 justify-content-center">
<div class="col-md-4">
<form method="post" action="/users">
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">用户名称</label>
<input type="username" class="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp">
<div id="emailHelp" class="form-text">We'll never
share your
email with anyone else.</div>
</div>
<div class="mb-3">
<label for="exampleInputPassword1"
class="form-label">密码</label>
<input type="password" class="form-control"
name="password"
id="exampleInputPassword1">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input"
id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check
me
out</label>
</div>
<textarea name="test" id="test" cols="30" rows="10"></textarea>
<button type="submit" class="btn btn-primary">添加</button>
</form>
<hr>
<ul class="list-group list-group-flush">
<li class="list-group-item">tom 123</li>
<li class="list-group-item">kite 456</li>
</ul>
</div>
</div>
</div>
</body>
</html>
Gin 博客项目-实现控制器和路由
控制器controller
package controller
import (
"pro04/dao"
"pro04/models"
"github.com/gin-gonic/gin"
)
func AddUser(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
user := models.User{
Username: username,
Password: password,
}
dao.Mgr.AddUser(&user)
}
func ListUser(c *gin.Context) {
c.HTML(200, "user.html", nil)
}
路由router
package routers
import (
"pro04/controller"
"github.com/gin-gonic/gin"
)
func Start() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.Static("/assets", "./assets")
e.GET("/users", controller.ListUser)
e.POST("/users", controller.AddUser)
e.Run()
}
Gin 博客项目-设计静态页面
{{define "header"}}
<header class="p-3 bg-dark text-white">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a class="navbar-brand" href="#">多课网</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li><a href="#" class="nav-link px-2 text-secondary">首页</a></li>
<li><a href="#" class="nav-link px-2 text-white">博客</a></li>
</ul>
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3">
<input type="search" class="form-control form-control-dark" placeholder="Search..." aria-label="Search">
</form>
<div class="text-end">
<button type="button" class="btn btn-outline-light me-2">登录</button>
<a type="button" class="btn btn-warning" href="/register">注册</a>
</div>
</div>
</div>
</header>
{{end}}
注册register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>用户注册</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div class="row justify-content-center mt-3">
<div class="col-md-4">
<form>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">用户名称</label>
<input type="email" class="form-control" id="exampleInputEmail1"
aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">用户密码</label>
<input type="password" class="form-control"
id="exampleInputPassword1">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input"
id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">记住我</label>
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
</div>
</div>
</div>
</body>
</html>
登录login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>用户注册</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div class="row justify-content-center mt-3">
<div class="col-md-4">
<form>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">用户名称</label>
<input type="email" class="form-control" id="exampleInputEmail1"
aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">用户密码</label>
<input type="password" class="form-control"
id="exampleInputPassword1">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input"
id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">记住我</label>
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
</div>
</div>
</div>
</body>
</html>
首页index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>Document</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div class="row justify-content-center mt-3">
<div class="col-md-4">
<p>首页...</p>
</div>
</div>
</div>
</body>
</html>
Gin 博客项目-用户注册
controller
func RegisterUser(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
user := model.User{
Username: username,
Password: password,
}
dao.Mgr.AddUser(&user)
c.Redirect(200, "/")
}
func GoRegister(c *gin.Context) {
c.HTML(200, "register.html", nil)
}
router
package router
import (
"blog/controller"
"github.com/gin-gonic/gin"
)
func Start() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.Static("/assets", "./assets")
e.GET("/", controller.Index)
e.POST("/register", controller.RegisterUser)
e.GET("/register", controller.GoRegister)
e.Run()
}
Gin 博客项目-用户登录
dao
package dao
import (
"blog/model"
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Manager interface {
AddUser(user *model.User)
Login(username string) model.User
}
type manager struct {
db *gorm.DB
}
var Mgr Manager
func init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to init db:", err)
}
Mgr = &manager{db: db}
db.AutoMigrate(&model.User{})
}
func (mgr *manager) AddUser(user *model.User) {
mgr.db.Create(user)
}
func (mgr *manager) Login(username string) model.User {
var user model.User
mgr.db.Where("username=?", username).First(&user)
return user
}
controller
package controller
import (
"blog/dao"
"blog/model"
"fmt"
"github.com/gin-gonic/gin"
)
func RegisterUser(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
user := model.User{
Username: username,
Password: password,
}
dao.Mgr.AddUser(&user)
c.Redirect(200, "/")
}
func GoRegister(c *gin.Context) {
c.HTML(200, "register.html", nil)
}
func GoLogin(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
func Login(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
fmt.Println(username)
u := dao.Mgr.Login(username)
if u.Username == "" {
c.HTML(200, "login.html", "用户名不存在!")
fmt.Println("用户名不存在!")
} else {
if u.Password != password {
fmt.Println("密码错误")
c.HTML(200, "login.html", "密码错误")
} else {
fmt.Println("登录成功")
c.Redirect(301, "/")
}
}
}
func Index(c *gin.Context) {
c.HTML(200, "index.html", nil)
}
func ListUser(c *gin.Context) {
c.HTML(200, "index.html", nil)
}
router
package router
import (
"blog/controller"
"github.com/gin-gonic/gin"
)
func Start() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.Static("/assets", "./assets")
e.GET("/login", controller.GoLogin)
e.POST("/login", controller.Login)
e.GET("/", controller.Index)
e.POST("/register", controller.RegisterUser)
e.GET("/register", controller.GoRegister)
e.Run()
}
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>用户注册</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div class="row justify-content-center mt-3">
<div class="col-md-4">
<p style="background-color: red;">{{.}}</p>
<form method="post" action="/login">
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">用户名称</label>
<input type="text" name="username" class="form-control" id="exampleInputEmail1"
aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">用户密码</label>
<input type="password" name="password" class="form-control"
id="exampleInputPassword1">
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
</div>
</div>
</body>
</html>
Gin 博客项目-集成markdown编辑器
下载mdeditor
https://pandao.github.io/editor.md/
集成
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/assets/editormd/css/editormd.css">
<title>Document</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div id="test-editormd">
<textarea style="display:none;"></textarea>
</div>
</div>
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "100%",
height : 640,
syncScrolling : "single",
path : "assets/editormd/lib/"
});
});
</body>
</html>
Gin 博客项目-创建博客模型和DAO
创建模型
type Post struct {
gorm.Model
Title string
Content string `gorm:"type:text"`
Tag string
}
创建DAO
package dao
import (
"blog/model"
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Manager interface {
AddUser(user *model.User)
Login(username string) model.User
// 博客操作
AddPost(post *model.Post)
GetAllPost() []model.Post
getPost(pid int) model.Post
}
type manager struct {
db *gorm.DB
}
var Mgr Manager
func init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/golang_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to init db:", err)
}
Mgr = &manager{db: db}
db.AutoMigrate(&model.User{})
db.AutoMigrate(&model.Post{})
}
func (mgr *manager) AddUser(user *model.User) {
mgr.db.Create(user)
}
func (mgr *manager) Login(username string) model.User {
var user model.User
mgr.db.Where("username=?", username).First(&user)
return user
}
// 博客操作
func (mgr *manager) AddPost(post *model.Post) {
mgr.db.Create(post)
}
func (mgr *manager) GetAllPost() []model.Post {
var posts = make([]model.Post, 10)
mgr.db.Find(&posts)
return posts
}
func (mgr *manager) getPost(pid int) model.Post {
var post model.Post
mgr.db.First(&post, pid)
return post
}
Gin 博客项目-创建博客控制器和路由
创建控制器
package controller
import (
"blog/dao"
"blog/model"
"fmt"
"github.com/gin-gonic/gin"
)
func RegisterUser(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
user := model.User{
Username: username,
Password: password,
}
dao.Mgr.AddUser(&user)
c.Redirect(200, "/")
}
func GoRegister(c *gin.Context) {
c.HTML(200, "register.html", nil)
}
func GoLogin(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
func Login(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
fmt.Println(username)
u := dao.Mgr.Login(username)
if u.Username == "" {
c.HTML(200, "login.html", "用户名不存在!")
fmt.Println("用户名不存在!")
} else {
if u.Password != password {
fmt.Println("密码错误")
c.HTML(200, "login.html", "密码错误")
} else {
fmt.Println("登录成功")
c.Redirect(301, "/")
}
}
}
func Index(c *gin.Context) {
c.HTML(200, "index.html", nil)
}
func ListUser(c *gin.Context) {
c.HTML(200, "index.html", nil)
}
func GetPostIndex(c *gin.Context) {
posts := dao.Mgr.GetAllPost()
c.HTML(200, "postIndex.html", posts)
}
func AddPost(c *gin.Context) {
title := c.PostForm("title")
tag := c.PostForm("tag")
content := c.PostForm("content")
post := model.Post{
Title: title,
Tag: tag,
Content: content,
}
dao.Mgr.AddPost(&post)
c.Redirect(302, "/post_index")
}
func GoAddPost(c *gin.Context) {
c.HTML(200, "post.html", nil)
}
创建路由
package router
import (
"blog/controller"
"github.com/gin-gonic/gin"
)
func Start() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.Static("/assets", "./assets")
e.GET("/login", controller.GoLogin)
e.POST("/login", controller.Login)
e.GET("/", controller.Index)
e.POST("/register", controller.RegisterUser)
e.GET("/register", controller.GoRegister)
e.GET("/post_index", controller.GetPostIndex)
e.POST("/post", controller.AddPost)
e.GET("/post", controller.GoAddPost)
e.Run()
}
Gin 博客项目-添加博客
添加博客页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/assets/editormd/css/editormd.css">
<title>添加博客</title>
</head>
<body>
<div class="container">
{{template "header"}}
<form action="/post" method="post">
<div class="row">
<div class="col-md-8">
<div id="test-editormd">
<textarea style="display:none;" name="content"></textarea>
</div>
</div>
<div class="col-md-4 mt-3">
<label for="title" class="form-label">请输入标题</label>
<input type="text" name="title" class="form-control" id="title"><br>
<label for="tag" class="form-label">请输入标签</label>
<input type="text" name="tag" class="form-control" id="tag"><br>
<button type="submit" class="btn btn-primary">添加</button>
</div>
</div>
</form>
</div>
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width : "100%",
height : 450,
syncScrolling : "single",
path : "assets/editormd/lib/",
watch : false,
});
});
</body>
</html>
Gin 博客项目-实现博客列表
博客列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>博客列表</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div class="row mt-3">
{{range $post := . -}}
<div class="col-md-6">
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4
shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<strong class="d-inline-block mb-2 text-primary">分类</strong>
<h3 class="mb-0">{{$post.Title}}</h3>
<div class="mb-1 text-muted">Nov 12</div>
<p class="card-text mb-auto">{{$post.Content}}</p>
<a href="#" class="stretched-link">阅读更多...</a>
</div>
<div class="col-auto d-none d-lg-block">
<svg class="bd-placeholder-img" width="200" height="250"
xmlns="http://www.w3.org/2000/svg" role="img"
aria-label="Placeholder: Thumbnail"
preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect
width="100%" height="100%" fill="#55595c"></rect><text x="50%"
y="50%" fill="#eceeef" dy=".3em">博客封面</text></svg>
</div>
</div>
</div>
{{- end}}
</div>
</div>
</body>
</html>
Gin 博客项目-实现博客详细
博客详细页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/assets/editormd/css/editormd.css">
<title>博客详细</title>
</head>
<body>
<div class="container">
{{template "header"}}
<div class="row">
<div class="col-md-12">
<h1>{{.Title}}</h1>
{{.Content}}
</div>
</div>
</div>
</body>
</html>
博客详细控制器
func PostDetail(c *gin.Context) {
s := c.Query("pid")
pid, _ := strconv.Atoi(s)
p := dao.Mgr.GetPost(pid)
content := blackfriday.Run([]byte(p.Content))
c.HTML(200, "detail.html", gin.H{
"Title": p.Title,
"Content": template.HTML(content),
})
}