場景:創業公司,資源少,任務重,需要一個最小化産品來講故事。
背景開發,采用 golang。web 架構采用 iris framework。資料庫用 MySql。
部署簡單,僅需要編譯出伺服器可執行程式,部署到公司購買的一台雲伺服器上即可。
用戶端APP直接通過HTTP API 通路伺服器。一個簡單的最小化産品就出來了。
func main() {
irisapp := iris.New()
// 設定調試模式
irisapp.Logger().SetLevel(irisloglevel)
//GFlog, _ = os.Create("./app.log")
//irisapp.Logger().SetOutput(GFlog)
// 捕獲相對于http産生的異常行為
irisapp.Use(recover.New())
//記錄請求日志
irisapp.Use(logger.New())
// 初始化配置
config := iris.WithConfiguration(iris.TOML("./iris.tml"))///main.go 同目錄
sess := sessions.New(sessions.Config{Cookie: "me-me-joe"})
RegRouter(irisapp, sess)
utils.LoadFromFile()
iris.RegisterOnInterrupt(func() {
orm.XormDb.Close()
utils.SaveToFile()
irisapp.Logger().Errorf ("RegisterOnInterrupt at %s !", time.Now() )
time.Sleep(10*time.Second)
})
irisapp.Run(iris.Addr(conf.CONFIG.Proj.Addr+":"+conf.CONFIG.Proj.Port), config)
}
其實,代碼中還是使用了配置檔案。作為你的第一版代碼,可以不用配置檔案,直接寫到代碼中也無不可。
iris web framework 的教程上有詳細的路由示例,比葫蘆畫瓢即可。
func RegRouter(app *iris.Application, sess *sessions.Sessions) {
。。。。
app.Use(CheckLogin)
// 跨域解決方案
crs := cors.New(cors.Options{
AllowedOrigins: []string{"51aiyaku.com","127.0.0.1","http://127.0.0.1:9336"},
AllowedMethods: []string{"HEAD", "GET", "POST", "DELETE"}, //"PUT", "PATCH",
AllowCredentials: true,
AllowedHeaders: []string{"*"},
Debug: true,
})
app.WrapRouter(crs.ServeHTTP)
reglogin := func(ctx iris.Context) {
ctx.Values().Set("session", sess.Start(ctx))
ctx.Values().Set("loginservice", new(services.LoginService))
ctx.Next()
}
// 登入
login := app.Party("/")
{
login.Get("/", controller.Loginw)
//login.Get("/captcha", controller.Login.Captcha)
//login.Post("/verify", controller.Login.Verify)
login.Post("/login", reglogin, controller.Login) //login 傳回token ,後面的所有接口都需要帶上token。Header裡Authorization: Bearer <token>
login.Post("/logout", reglogin, controller.Logout)
}
。。。。
}
上面代碼中,每個路由的處理代碼放到了 controller 中。其實,為了簡單化,你可以在main.go檔案中直接寫一個 handler 函數。
type LoginCtrl struct {
Ctx iris.Context // mvc 執行個體的 BeginRequest(ctx)自動初始化
Session *sessions.Session // mvc 執行個體的Register 綁定全局sessions執行個體到這裡
ServLogin *services.LoginService // --> Model model.User
}
func Loginw(ctx iris.Context) {
login := new(LoginCtrl)
login.Ctx = ctx
login.Loginw()
login=nil
}
func Login(ctx iris.Context) {
login := new(LoginCtrl)
login.Ctx = ctx
login.Session = ctx.Values().Get("session").(*sessions.Session)
login.ServLogin = ctx.Values().Get("loginservice").(*services.LoginService)
login.Login()
login=nil
}
//Get:/login -- login web page
func (c *LoginCtrl) Loginw() {
//c.Ctx.WriteString("未登入")
c.Ctx.JSON(iris.Map{
"Msg": "未登入",
})
return
}
//POST: /user/login
func (c *LoginCtrl) Login() {
// 登入參數
var req dto.LoginReq
// 參數綁定
if err := c.Ctx.ReadForm(&req); err != nil {
CommonErr(c.Ctx,err.Error())
return
}
// 參數校驗
v := validate.Struct(req)
if !v.Validate() {
ValidateError(c.Ctx,v.Errors.One())
return
}
//if !verifyCaptcha(req.Captcha,req.IdKey) {
// c.Ctx.JSON(dto.JsonResult{
// Code: -1,
// Msg: "驗證碼錯誤",
// })
// return
//}
// 系統登入
token, err := c.ServLogin.UserLogin(req.UserName, req.Password, c.Ctx)
fmt.Println(token)
//token, err := c.Model.Login(req.UserName, req.Password, c.Ctx)
if err != nil {
CommonErr(c.Ctx,err.Error())
return
}
// 登入成功
c.Ctx.JSON(dto.JsonResult{
Code: MMCodeOK,
Msg: "登入成功",
Data: iris.Map{
"access_token": token,
},
})
}
示例代碼中 Login,先從 request 中讀取 form 格式的請求參數。然後用validate去校驗參數,即LoginReq struct 定義的字段不能缺失或資料異常。按請求參數去查詢資料庫,如果使用者存在且密碼正确則傳回token令牌。controller 可以請求service層,也可以直接通路資料層。其實對于最小化産品,架構層數越少越好。随着時間資源越多,你越有時間重構軟體架構,可以分層,可以微服務化,可以加緩存,可以自動化注冊服務與發現服務等等。
type AppUser struct {
Id int64 `xorm:"pk autoincr INT(11)"`
Username string `xorm:"index VARCHAR(20)"`
Pwd string `xorm:"not null VARCHAR(20)"`
IsDeleted bool `xorm:"default b'0' BIT(1)"`
Memo string `xorm:"VARCHAR(40)"`
Phone string `xorm:"VARCHAR(20)"`
Pwdhash string `xorm:"VARCHAR(64)"`
}
func (u *AppUser) Login(username string, password string, c iris.Context) (string, error) {
has, err := orm.XormDb.Select("username,pwd").Where("username=?", username).Get(u)
if err != nil && !has {
return "", errors.New("使用者名或者密碼不正确")
}
if u.Pwd != password {
return "", errors.New("密碼不正确")
}
// 判斷目前使用者狀态
if u.IsDeleted != false {
return "", errors.New("您的賬号已被禁用,請聯系管理者")
}
// 更新登入時間、登入IP
//orm.XormDb.ID(u.Id).Update(&AppUser{LoginDate: time.Now(), LoginIp: c.RemoteAddr(), ModifyDate: time.Now()})
// 生成Token
token, _ := utils.GenerateToken(int(u.Id), u.Username, u.Username)
return token, nil
}
通路資料庫,這裡使用了 xorm 庫,對于最小化産品,你可以直接使用 sql 語句。還有使用者密碼将來不存明碼,而是存加密後的密碼。
對于一個創業公司,千萬不要一上來就一個分布式微服務架構的高大上設計。可能你還沒有完善架構,公司已經改弦更張了。