go语言的魔幻旅行38-gin、Gorm整合实现restfui api
空談誤國,實干興邦,零碎的知識掌握的再多,如果不將它們整個的串聯起來就是一堆沒有頭緒的點,為了充分的將之前講解的相關的gin、gorm相關的知識有邏輯的串聯起來,接下來的教程將實現一個具有restfui風格的api,需要具備的知識點有gin、gorm、viper等。
項目的包結構說明
one- conf- config- enum- handler- model- query- repository- resp- service- utilsgo.modHandler.gomain.goconf目錄主要存放項目的配置文件config.yaml, config目錄主要存放config.go加載項目的配置文件的實現,enum存放定義的常量、model存放實體類、repository存放數據庫交互接口、service存放數據庫接口調用類、handler存放對應的處理器、query包和utils分貝存放分頁查詢實體和工具類,Handler.go實現配置DB對象、handler對象、配置文件初始化,main.go配置路由。
配置文件相關
one/conf/config.yaml
mode: debug port: :9090 url: http://127.0.0.1:9090 max_check_count: 10 database:name: testhost: 127.0.0.1:3306username: rootpassword: rootone/config/config.go
import ("github.com/fsnotify/fsnotify""github.com/spf13/viper""log" )const PAGE_SIZE int = 10type Config struct {Name string }func Init(name string) error {c := Config{Name: name,}//初始化配置文件if err := c.initConfig(); err != nil {return err}//監控配置文件變化并熱加載程序c.watchConfig()return nil }func (c *Config) initConfig() error {//如果指定了配置文件,則解析指定的配置文件if c.Name != "" {viper.SetConfigFile(c.Name)} else {//如果沒有指定配置文件,則解析默認的配置文件viper.AddConfigPath("conf")viper.SetConfigName("config")}// 設置配置文件格式為YAMLviper.SetConfigType("yaml")if err := viper.ReadInConfig(); err != nil { // viper解析配置文件return err}return nil }// 監控配置文件變化并熱加載程序 func (c *Config) watchConfig() {viper.WatchConfig()viper.OnConfigChange(func(e fsnotify.Event) {log.Printf("Config file changed: %s", e.Name)}) }數據庫交互實現
one/repository/user.go
import ("fmt""gorm.io/gorm""one/model""one/query""one/utils" )type UserRepository struct {DB *gorm.DB }type UserRepoInterface interface {List(req *query.ListQuery) (users []*model.User, err error)GetTotal(req *query.ListQuery) (total int64, err error)Get(user model.User) (*model.User, error)Exist(user model.User) *model.UserExistByUserID(id string) *model.UserExistByMobile(mobile string) *model.UserAdd(user model.User) (*model.User, error)Edit(user model.User) (bool, error)Delete(u model.User) (bool, error) }func (repo *UserRepository) List(req *query.ListQuery) (users []*model.User, err error) {fmt.Println(req)db := repo.DBlimit, offset := utils.Page(req.PageSize, req.Page) // 分頁if err := db.Order("id desc").Limit(limit).Offset(offset).Find(&users).Error; err != nil {return nil, err}return users, nil }func (repo *UserRepository) GetTotal(req *query.ListQuery) (total int64, err error) {var users []model.Userdb := repo.DBif err := db.Find(&users).Count(&total).Error; err != nil {return total, err}return total, nil }func (repo *UserRepository) Get(user model.User) (*model.User, error) {if err := repo.DB.Where(&user).Find(&user).Error; err != nil {return nil, err}return &user, nil }func (repo *UserRepository) Exist(user model.User) *model.User {var count intrepo.DB.Find(&user).Where("nick_name = ?", user.NickName)if count > 0 {return &user}return nil }func (repo *UserRepository) ExistByMobile(mobile string) *model.User {var count intvar user model.Userrepo.DB.Find(&user).Where("mobile = ?", mobile)if count > 0 {return &user}return nil }func (repo *UserRepository) ExistByUserID(id string) *model.User {var user model.Userrepo.DB.Where("user_id = ?", id).First(&user)return &user }func (repo *UserRepository) Add(user model.User) (*model.User, error) {if exist := repo.Exist(user); exist != nil {return nil, fmt.Errorf("用戶注冊已存在")}err := repo.DB.Create(&user).Errorif err != nil {return nil, fmt.Errorf("用戶注冊失敗")}return &user, nil }func (repo *UserRepository) Edit(user model.User) (bool, error) {err := repo.DB.Model(&user).Where("user_id=?", user.UserId).Updates(map[string]interface{}{"nick_name": user.NickName, "mobile": user.Mobile, "address": user.Address}).Error//err := repo.DB.Save(&user).Errorif err != nil {return false, err}return true, nil }func (repo *UserRepository) Delete(u model.User) (bool, error) {err := repo.DB.Model(&u).Where("user_id=?", u.UserId).Update("is_deleted", u.IsDeleted).Errorif err != nil {return false, err}return true, nil }one/service/user.go
import ("errors""fmt"uuid "github.com/satori/go.uuid""one/config""one/model""one/query""one/repository""one/utils" )type UserSrv interface {List(req *query.ListQuery) (users []*model.User, err error)GetTotal(req *query.ListQuery) (total int64, err error)Get(user model.User) (*model.User, error)Exist(user model.User) *model.UserExistByUserID(id string) *model.UserAdd(user model.User) (*model.User, error)Edit(user model.User) (bool, error)Delete(id string) (bool, error) }type UserService struct {Repo repository.UserRepoInterface }func (srv *UserService) List(req *query.ListQuery) (users []*model.User, err error) {if req.PageSize < 1 {req.PageSize = config.PAGE_SIZE}return srv.Repo.List(req) } func (srv *UserService) GetTotal(req *query.ListQuery) (total int64, err error) {return srv.Repo.GetTotal(req) } func (srv *UserService) Get(user model.User) (*model.User, error) {return srv.Repo.Get(user) } func (srv *UserService) Exist(user model.User) *model.User {return srv.Repo.Exist(user) }func (srv *UserService) ExistByUserID(id string) *model.User {return srv.Repo.ExistByUserID(id) }func (srv *UserService) Add(user model.User) (*model.User, error) {//根據手機號判斷是否存在用戶result := srv.Repo.ExistByMobile(user.Mobile)if result != nil {return nil, errors.New("用戶已經存在")}user.UserId = uuid.NewV4().String()if user.Password == "" {user.Password = utils.Md5("123456")}user.IsDeleted = falseuser.IsLocked = falsereturn srv.Repo.Add(user) } func (srv *UserService) Edit(user model.User) (bool, error) {if user.UserId == "" {return false, fmt.Errorf("參數錯誤")}exist := srv.Repo.ExistByUserID(user.UserId)if exist == nil {return false, errors.New("參數錯誤")}exist.NickName = user.NickNameexist.Mobile = user.Mobileexist.Address = user.Addressreturn srv.Repo.Edit(*exist) } func (srv *UserService) Delete(id string) (bool, error) {if id == "" {return false, errors.New("參數錯誤")}user := srv.ExistByUserID(id)if user == nil {return false, errors.New("參數錯誤")}user.IsDeleted = !user.IsDeletedreturn srv.Repo.Delete(*user) }one/handler/user.go
import ("github.com/gin-gonic/gin""net/http""one/enum""one/model""one/query""one/resp""one/service" )type UserHandler struct {UserSrv service.UserSrv }func (h *UserHandler) GetEntity(result model.User) resp.User {return resp.User{Id: result.UserId,Key: result.UserId,UserId: result.UserId,NickName: result.NickName,Mobile: result.Mobile,Address: result.Address,IsDeleted: result.IsDeleted,IsLocked: result.IsLocked,} }func (h *UserHandler) UserInfoHandler(c *gin.Context) {entity := resp.Entity{Code: int(enum.OperateFail),Msg: enum.OperateFail.String(),Total: 0,TotalPage: 1,Data: nil,}userId := c.Param("id")if userId == "" {c.JSON(http.StatusInternalServerError, gin.H{"entity": entity})return}u := model.User{UserId: userId,}result, err := h.UserSrv.Get(u)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"entity": entity})return}r := h.GetEntity(*result)entity = resp.Entity{Code: http.StatusOK,Msg: "OK",Total: 0,TotalPage: 0,Data: r,}c.JSON(http.StatusOK, gin.H{"entity": entity}) }func (h *UserHandler) UserListHandler(c *gin.Context) {var q query.ListQueryentity := resp.Entity{Code: int(enum.OperateFail),Msg: enum.OperateFail.String(),Total: 0,TotalPage: 1,Data: nil,}err := c.ShouldBindQuery(&q)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"entity": entity})return}list, err := h.UserSrv.List(&q)total, err := h.UserSrv.GetTotal(&q)if err != nil {panic(err)}if q.PageSize == 0 {q.PageSize = 5}ret := int(total) % q.PageSizeret2 := int(total) / q.PageSizetotalPage := 0if ret == 0 {totalPage = ret2} else {totalPage = ret2 + 1}var newList []*resp.Userfor _, item := range list {r := h.GetEntity(*item)newList = append(newList, &r)}entity = resp.Entity{Code: http.StatusOK,Msg: "OK",Total: total,TotalPage: totalPage,Data: newList,}c.JSON(http.StatusOK, gin.H{"entity": entity}) }func (h *UserHandler) AddUserHandler(c *gin.Context) {entity := resp.Entity{Code: int(enum.OperateFail),Msg: enum.OperateFail.String(),Total: 0,Data: nil,}u := model.User{}err := c.ShouldBindJSON(&u)if err != nil {c.JSON(http.StatusOK, gin.H{"entity": entity})return}r, err := h.UserSrv.Add(u)if err != nil {entity.Msg = err.Error()return}if r.UserId == "" {c.JSON(http.StatusOK, gin.H{"entity": entity})return}entity.Code = int(enum.OperateOk)entity.Msg = enum.OperateOk.String()c.JSON(http.StatusOK, gin.H{"entity": entity})}func (h *UserHandler) EditUserHandler(c *gin.Context) {u := model.User{}entity := resp.Entity{Code: int(enum.OperateFail),Msg: enum.OperateFail.String(),Total: 0,Data: nil,}err := c.ShouldBindJSON(&u)if err != nil {c.JSON(http.StatusOK, gin.H{"entity": entity})return}b, err := h.UserSrv.Edit(u)if err != nil {c.JSON(http.StatusOK, gin.H{"entity": entity})return}if b {entity.Code = int(enum.OperateOk)entity.Msg = enum.OperateOk.String()c.JSON(http.StatusOK, gin.H{"entity": entity})}}func (h *UserHandler) DeleteUserHandler(c *gin.Context) {id := c.Param("id")b, err := h.UserSrv.Delete(id)entity := resp.Entity{Code: int(enum.OperateFail),Msg: enum.OperateFail.String(),Total: 0,Data: nil,}if err != nil {c.JSON(http.StatusOK, gin.H{"entity": entity})return}if b {entity.Code = int(enum.OperateOk)entity.Msg = enum.OperateOk.String()c.JSON(http.StatusOK, gin.H{"entity": entity})} }涉及到的響應實體類、工具類、常量類及路由文件
one/utils/utils.go
import ("crypto/md5""fmt""io""one/config""time" )// Page 分頁 func Page(Limit, Page int) (limit, offset int) {if Limit > 0 {limit = Limit} else {limit = config.PAGE_SIZE}if Page > 0 {offset = (Page - 1) * limit} else {offset = -1}return limit, offset }// Sort 排序 // 默認 created_at desc func Sort(Sort string) (sort string) {if Sort != "" {sort = Sort} else {sort = "create_at desc"}return sort }const TimeLayout = "2006-01-02 15:04:05"var (Local = time.FixedZone("CST", 8*3600) )func GetNow() string {now := time.Now().In(Local).Format(TimeLayout)return now }func TimeFormat(s string) string {result, err := time.ParseInLocation(TimeLayout, s, time.Local)if err != nil {panic(err)}fmt.Println(result)return result.In(Local).Format(TimeLayout)}func Md5(str string) string {w := md5.New()io.WriteString(w, str)md5str := fmt.Sprintf("%x", w.Sum(nil))return md5str }one/enum/user_enum.go
type ResponseType intconst (OperateOk ResponseType = 200OperateFail ResponseType = 500 )func (p ResponseType) String() string {switch p {case OperateOk:return "Ok"case OperateFail:return "Fail"default:return "UNKNOWN"} }one/resp/user.go
type User struct {Id string `json:"id"`Key string `json:"key"`UserId string `json:"userId" gorm:"column:user_id"`NickName string `json:"nickName" gorm:"column:nick_name"`Mobile string `json:"mobile" gorm:"column:mobile" binding:"required"`Address string `json:"address" gorm:"column:address"`IsDeleted bool `json:"isDeleted" gorm:"column:is_deleted"`IsLocked bool `json:"isLocked" gorm:"column:is_locked"` }one/Handler.go
package mainimport ("fmt""github.com/spf13/viper""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/schema""log""one/config""one/handler""one/model""one/repository""one/service" )var (DB *gorm.DBBannerHandler handler.BannerHandlerCategoryHandler handler.CategoryHandlerOrderHandler handler.OrderHandlerProductHandler handler.ProductHandlerUserHandler handler.UserHandler )func initViper() {if err := config.Init(""); err != nil {panic(err)} }func initDB() {fmt.Println("數據庫 init")var err errorconf := &model.DBConf{Host: viper.GetString("database.host"),User: viper.GetString("database.username"),Password: viper.GetString("database.password"),DbName: viper.GetString("database.name"),}config := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&charset=utf8&parseTime=%t&loc=%s",conf.User,conf.Password,conf.Host,conf.DbName,true,"Local")DB, err = gorm.Open(mysql.Open(config), &gorm.Config{NamingStrategy: schema.NamingStrategy{//禁用表的復數形式SingularTable: true,},})if err != nil {log.Fatalf("connect error: %v\n", err)}fmt.Println("數據庫 init 結束...") }func initHandler() {UserHandler = handler.UserHandler{UserSrv: &service.UserService{Repo: &repository.UserRepository{DB: DB,},}} }func init() {initViper()initDB()initHandler() }主啟動類
package mainimport ("github.com/gin-gonic/gin""github.com/spf13/viper""net/http" )func Cors() gin.HandlerFunc {return func(c *gin.Context) {method := c.Request.Methodc.Header("Access-Control-Allow-Origin", "*")c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")c.Header("Access-Control-Allow-Credentials", "true")if method == "OPTIONS" {c.AbortWithStatus(http.StatusNoContent)}// 處理請求c.Next()} }func main() {r := gin.Default()r.Use(Cors())gin.SetMode(viper.GetString("mode"))user := r.Group("/api/user"){user.GET("/list", UserHandler.UserListHandler)user.GET("/info/:id", UserHandler.UserInfoHandler)user.POST("/add", UserHandler.AddUserHandler)user.POST("/edit", UserHandler.EditUserHandler)user.POST("/delete/:id", UserHandler.DeleteUserHandler)}port := viper.GetString("port")r.Run(port) }啟動
go build main.go Handler.go
小結
上面簡單的實現了一個restfui風格的架構,重在實現思想實現,可能實際運用的時候根據項目的不同有所差異,但是上述的知識基本實現了gin、grom 知識點的整合。
總結
以上是生活随笔為你收集整理的go语言的魔幻旅行38-gin、Gorm整合实现restfui api的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何用linux指令集,linux –
- 下一篇: 高端物理学名词_什么是物理高端备课?