天天看點

網站最小化背景Golang開發實踐

作者:宇宙雙黑洞

場景:創業公司,資源少,任務重,需要一個最小化産品來講故事。

背景開發,采用 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 語句。還有使用者密碼将來不存明碼,而是存加密後的密碼。

對于一個創業公司,千萬不要一上來就一個分布式微服務架構的高大上設計。可能你還沒有完善架構,公司已經改弦更張了。

繼續閱讀