這一系列文章主要是讓想進階服務端開發的前端小夥伴們一個入門參考,後端大佬能夠不用看了,極可能是浪費你的時間。css
上一篇文章基於Golang的微服務——Micro實踐(二)遇到問題了,我得先理解下,就當是佔了個坑吧,後面我理解透了再補上。 這篇記錄下我嘗試Gin框架遇到的問題。html
Gin中文文檔前端
學習Golang的過程當中,雖而後端語言的大體思路是差很少的,可是在語言細節和語法上跟以前接觸的Laravel, Node.js仍是有區別的.多熟悉,多對比,更容易知道其因此然了。mysql
Gin 是一個用 Go (Golang) 編寫的 HTTP web 框架。類比與laravel
Node.js
的web框架: express, koa
git
PHP
的web框架:laravel, lumen , yii
程序員
Python
的web框架 Django , Flask
github
只要你以前接觸過這些,這麼一類比你確定就秒懂了。web
官網右上角能夠切換語言 sql
Golang開發環境參照我前面寫的基於Golang的微服務——上手篇文章配置就行
新建項目目錄 tech
, 我以前寫Laravel的時候感受代碼和項目結構真的很優雅,就按照Laravel的項目結構來了。
tech
-app
-Controllers // 控制器函數
-Middleware
-Models // 數據模型
-config // 配置文件
-databse // 數據庫
-mysql.go
-public // 靜態資源目錄
-css
-imgs
-js
-resources // 項目資源,好比放scss,經過編譯轉化成項目用的css
-lang
-sass
-js
-routes // 路由
-web.go
-views // 視圖文件,這個項目會涉及到後端模板渲染和 API開發
-usr
-index.tmpl
-post
-index.tmpl
-home
-index.tmpl
.env // 配置文件
.gitignore
main.go // 入口文件
README.md
複製代碼
附一張項目結構圖:
main.go
package main
import (
"os"
_ "tech/config"
_ "tech/database"
"tech/routes"
)
func main() {
r := routes.InitRouter()
port := os.Getenv("HTTP_PORT")
r.Run(":" + port) // 監聽並在 0.0.0.0:8080 上啓動服務
}
複製代碼
先貼下入口文件裏的內容,而後根據入口文件從上到下講解。
_ tech/config
這裏導入的是配置文件,tech/config/config.go
文件源碼以下:
package config
import (
"github.com/joho/godotenv"
"log"
"os"
)
func init(){
err := godotenv.Load() // 載入 godotenv
if err != nil {
log.Fatal("Error loading .env file")
}
PORT := os.Getenv("HTTP_PORT") // 獲取.env配置文件裏的HTTP_POTRT值
log.Print(PORT)
}
複製代碼
這裏涉及到了一個 本地.env 文件讀取的包godotenv,能夠獲取到項目裏.env文件預設置的值
// 這裏是 .env文件內容
HTTP_PORT=8090
AUTHOR=winyh
複製代碼
這裏經過簡單的配置文件演示項目的一些配置,好比項目端口號,數據庫開發環境的數據庫信息,均可以放到.env 若是正式環境的數據庫信息放在.env裏就必定要注意了,把這個配置文件添加到.gitingore忽略文件裏,避免上傳到遠程代碼倉庫了。
_ tech/database
這裏是數據庫連接功能的實現,tech/database/mysql.go
源碼以下:
package database
import (
"fmt"
_ "github.com/go-sql-driver/mysql" //加載mysql驅動
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
var DB *gorm.DB
func init() {
var err error
DB, err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/tech?charset=utf8&parseTime=True&loc=Local&timeout=10ms")
if err != nil {
fmt.Printf("mysql connect error %v", err)
}
if DB.Error != nil {
fmt.Printf("database error %v", DB.Error)
}
}
複製代碼
項目數據庫選的是mysql,因此須要先安裝mysql的驅動包
go get github.com/go-sql-driver/mysql
複製代碼
寫原生sql語句臺痛苦了,因此引入一個ORM(簡單粗暴的理解爲一系列的原生sql 語句的封裝,可讓你操做數據庫更簡便,會想起以前寫Larvel,它自帶的ORM用着是真 爽,基本上是要啥有啥)
go get github.com/jinzhu/gorm
複製代碼
數據庫的連接主要是下面這句,其中
root:是你本機的mysql用戶名 123456: 是你本機的數據庫密碼 127.0.0.1:3306:表示數據庫地址和端口號 tech:是你本機創建的數據庫名稱
DB, err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/tech?charset=utf8&parseTime=True&loc=Local&timeout=10ms")
複製代碼
tech/routes
入口文件裏引入了routes這個包,tech/routes/web.go
源碼以下:
package routes
import (
"github.com/gin-gonic/gin"
"tech/app/Controllers"
)
func InitRouter() *gin.Engine {
r := gin.Default()
r.Static("/public", "./public") // 靜態文件服務
r.LoadHTMLGlob("views/**/*") // 載入html模板目錄
// web路由
r.GET("/", Controllers.Home)
r.GET("/about", Controllers.About)
r.GET("/post/index", Controllers.Post)
// 簡單的路由組: v1
v1 := r.Group("/api")
{
v1.GET("/ping", Controllers.Ping)
v1.POST("/user/create", Controllers.UserCreate)
v1.POST("/user/delete", Controllers.UserDestroy)
v1.POST("/user/update", Controllers.UserUpdate)
v1.POST("/users", Controllers.UserFindAll)
}
return r
}
複製代碼
後端項目基本都是MVC的模式,微服務架構可能會更簡單一點。路由是一個項目對外提供服務的入口,全部的前端請求從這裏進入到指定的處理函數。處理函數會操做業務邏輯,返回數據和給出響應,完成一次請求。 這裏引入的包"github.com/gin-gonic/gin"
就是Gin框架的包。其次引入了咱們本身編寫的本地包"tech/app/Controllers"
我把這個路由文件裏實現的功能分爲了兩類,一類是提供web服務的路由(也就是返回模板渲染後的html文件),一類是提供 Restful API 服務的路由。路由地址匹配咱們的處理函數。
上面每一個路由地址都關聯有一個處理函數,咱們把它叫作控制器。講解其中的 Controllers/Home.go
控制器內容,源碼以下:
package Controllers
import (
"github.com/gin-gonic/gin"
"net/http"
)
func Home(c *gin.Context) {
c.HTML(http.StatusOK, "home/index.tmpl", gin.H{
"title": "這是首頁",
})
}
複製代碼
基本邏輯是獲取到請求的上下文,用Gin提供的c.HTML返回須要渲染返回到前臺的數據。這裏的title
數據能夠手動定義,也能夠在數據庫裏查詢到後再放入模板文件裏渲染,這樣看到的數據就是咱們數據庫裏存放的數據了。
視圖是返回給前端的頁面模板渲染文件,後端從數據庫查詢到的或者自定義的數據,均可以經過變量的方式渲染到模板裏面,視圖從後端到前端可見會經歷兩次渲染,第一次是後端把數據填充進去,而後用模板引擎把模板文件渲染成html,發送到前端的時候,瀏覽器識別到 content-Type:'text/html',再次經過瀏覽器的渲染,把html標籤渲染成用戶可見的界面。第一次渲染是在服務器渲染的,第二次渲染是在前端渲染的。參照這裏你應該能夠理解前端常說的爲了作SEO優化,須要用Node.js作服務端渲染是什麼個意思可,能夠參照我以前寫的服務端渲染文章。 這裏貼一下 views/home/index.tmpl
模板文件的源碼:
{{ define "home/index.tmpl" }}
<html>
<head>
<meta charset="UTF-8">
<title>Gin 框架測試</title>
</head>
<body>
<div class="container">
<h1>這是一個測試頁面</h1>
<p>{{ .title }}</p>
</div>
</body>
</html>
{{ end }}
複製代碼
再列舉一個 API服務的控制器,Controllers/Api.go
源碼以下:
package Controllers
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"net/http"
"tech/app/Models"
)
type Admins struct {
gorm.Model
Name string `json:"name" binding:"required"`
Password string `json:"password" binding:"required"`
Mobile string `json:"mobile" binding:"required"`
}
func Ping(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
}
func UserCreate(c *gin.Context) {
var json Models.Admins // 定義json 變量 數據結構類型 爲 Models.Admins
err := c.BindJSON(&json) // 獲取前臺傳過來的 json數據
if err != nil {
fmt.Printf("mysql connect error %v", err)
}
id, err := json.Insert()
if err != nil {
fmt.Printf("database error %v", err)
fmt.Printf("database error %v", id)
return
}
c.JSON(200, gin.H{ // 反饋給前臺的信息,同時返回最新建立的一條數據的Id
"status": true,
"id":id,
"message":"建立成功",
})
}
func UserDestroy(c *gin.Context) {
var json Models.Admins
err := c.BindJSON(&json)
if err != nil {
fmt.Printf("mysql connect error %v", err)
return
}
json.Destroy()
c.JSON(200, gin.H{
"status": true,
"message":"刪除成功",
})
}
func UserUpdate(c *gin.Context) {
var json Models.Admins
err := c.BindJSON(&json)
if err != nil {
fmt.Printf("mysql connect error %v", err)
}
id, err := json.Update(3)
if err != nil {
fmt.Printf("database error %v", err)
fmt.Printf("database error %v", id)
return
}
c.JSON(200, gin.H{
"status": true,
"id":id,
"message":"更新成功",
})
}
func UserFindAll(c *gin.Context) {
var json Models.Admins
err := c.BindJSON(&json)
if err != nil {
fmt.Printf("mysql connect error %v", err)
}
result, err := json.FindAll()
if err != nil {
c.JSON(http.StatusOK, gin.H{
"status": false,
"message": "抱歉未找到相關信息",
})
return
}
c.JSON(200, gin.H{
"status": true,
"data": result,
"message":"查詢成功",
})
}
複製代碼
這個文件裏完成了後端基本的增刪改查功能。內容相對多點,列舉其中的用戶新建來講一下: UserCreate 函數會接收到前臺傳過來的json數據,而後 調用 id, err := json.Insert()
作數據入庫的操做。這裏涉及到 Insert函數,也就是接下來要降到的數據模型
數據模型是數據的抽象,能夠提供一些數據操做的函數封裝。 app/Models/admin.go
源碼以下:
package Models
import (
"github.com/jinzhu/gorm"
. "tech/database"
)
type Admins struct {
gorm.Model // 這裏的配置可讓ORM 自動維護 時間戳字段,很爽有木有
Name string `json:"name" binding:"required"`
Password string `json:"password" binding:"required"`
Mobile string `json:"mobile" binding:"required"`
}
// Insert 新增admin用戶
func (admin *Admins) Insert() (userID uint, err error) {
result := DB.Create(&admin) // 這裏的DB變量是 database 包裏定義的,Create 函數是 gorm包的建立數據API
userID = admin.ID
if result.Error != nil {
err = result.Error
}
return // 返回新建數據的id 和 錯誤信息,在控制器裏接收
}
// Destroy 刪除admin用戶
func (admin *Admins) Destroy() (err error) {
result := DB.Delete(&admin)
if result.Error != nil {
err = result.Error
}
return
}
// Update 修改admin用戶
func (admin *Admins) Update(id int64) (user Admins, err error) {
result := DB.Model(&admin).Where("id = ?", id).Updates(&admin)
if result.Error != nil {
err = result.Error
}
return
}
// FindOne 查詢admin用戶
func (admin *Admins) FindAll() (admins []Admins, err error) {
result := DB.Find(&admins) // 這裏的 &admins 跟返回參數要一致
if result.Error != nil {
err = result.Error
return
}
return
}
複製代碼
在項目根目錄下運行
go run main.go
複製代碼
就能夠在本地訪問配置的路由了
localhost:8090/api/ping
複製代碼
項目最後放上幾個測試截圖
控制檯啓動信息
文章頁面
用戶數據查詢,我這裏用的是POST方法,查詢接口建議你們遵循Restful Api 規則,用GET方法
數據新增,返回了當前新增數據的ID,前端端基於JSON數據格式傳輸交互
項目裏的基本邏輯就是這樣了,忽然感受寫文章仍是很浪費時間的,本身寫的質量也很低,權當跟你們交流吧,把本身的缺點暴露出來,有人指點交流,能學到東西就是進步。
反思了下本身,之前基本是什麼技術都想學,後來發現這樣不現實,要找準方向,將精力集中,讓時間和努力發揮出它最大的價值而不至於分散了。
後面本身仍是主攻前端技術,深刻研究Golang 和 Node.js ,歡迎你們一塊兒交流。Linux應該算是基礎知識了,但願在這個平臺能夠認識更多優秀的朋友。
再仔細的反思了下本身,技術棧深度不夠,之後太基礎的文章我可能只會本身作筆記不會寫出來了,質量過低浪費了本身精力,也浪費了點擊進來看的人時間,時間是多麼寶貴的東西啊,於心不忍。看到一篇很不錯的文章。
總結幾點我很認同的觀點:
最寶貴的財富並非錢,而是你的時間。
25~35 歲是每一個人最寶貴的時光,應該用在刀刃上。
25~35 歲是每一個人最寶貴的時光,應該用在刀刃上。
25~35 歲是每一個人最寶貴的時光,應該用在刀刃上。
不會交流的計算機人員30歲之後通常會遇到不少瓶頸
原文連接左耳朵耗子:程序員如何用技術變現? 博主博客:酷殼