A,ginProject introduction

Gin framework is very easy for those who write Go language to get started, which is similar to the Flask framework in Python. You need to find third-party packages for everything and then create directory structures according to your own experience. For those who have no project experience, this is really different from the beego framework, which already has a clear directory structure. Sometimes we can say gin is just a package, not a framework. The flexibility of free assembly underscores the importance of our experience as developers. How to build a gIN-API project is difficult.

The following is a set of basic GIN-API framework based on my back-end experience and MVC thinking. For your reference, we feel better 👍

  • Github source address
  • Yards cloud address

Dependency packages to be installed

  • Gin framework package

    go get -u github.com/gin-gonic/gin
    Copy the code
  • Gorm database package

    go get -u gorm.io/gorm
    go get -u gorm.io/driver/mysql
    Copy the code
  • Data verification package

    go get github.com/go-playground/validator
    Copy the code
  • Token authentication package

    go get -u github.com/dgrijalva/jwt-go
    Copy the code
  • Log Management package

    go get -u github.com/sirupsen/logrus
    go get -u github.com/lestrrat-go/file-rotatelogs
    go get -u github.com/rifflock/lfshook
    Copy the code
  • Configuration file package

    go get -u github.com/spf13/viper
    Copy the code

3. Project configuration file

  • 1. Create the configuration parameters required by the project in the config/application.yml file

    server:
      port: 9000
    # database configuration
    datasource:
      driverName: mysql
      host: localhost
      port: "3306"
      database: gin_admin_api
      username: root
      password: 123456
      charset: utf8mb4
      loc: Asia/Shanghai
    Copy the code
  • 2. Define an initial configuration file in main.go

    // Initialize the configuration
    func InitConfig(a) {
    	workDir, _ := os.Getwd()
    	viper.SetConfigName("application")
    	viper.SetConfigType("yml")
    	viper.AddConfigPath(path.Join(workDir, "config"))
    	// Or use full path
    	//viper.AddConfigPath(path.Join(workDir, "config/application.yml"))
    	err := viper.ReadInConfig()
    	iferr ! =nil {
    		fmt.Print("Error obtaining configuration file")
    		panic(err)
    	}
    }
    Copy the code
  • 3. Call the initialization configuration file in init

    func init(a) {
    	InitConfig()
    }
    Copy the code
  • 4. Test whether the configuration file is successful

    func main(a) {
    	router := gin.Default()
    	router.GET("/".func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{
    			"code": 1,
    		})
    	})
    	port := viper.GetString("server.port")
    	fmt.Println("Current port", port)
    	ifport ! ="" {
    		router.Run(":" + port)
    	} else {
    		router.Run()
    	}
    }
    Copy the code
  • 5, or you can go to the common/config file separately

    package common
    
    import (
    	"fmt"
    	"github.com/spf13/viper"
    	"os"
    	"path"
    )
    
    // Initialize the configuration
    func InitConfig(a) {
    	workDir, _ := os.Getwd()
    	viper.SetConfigName("application")
    	viper.SetConfigType("yml")
    	viper.AddConfigPath(path.Join(workDir, "config"))
    	// Or use full path
    	//viper.AddConfigPath(path.Join(workDir, "config/application.yml"))
    	err := viper.ReadInConfig()
    	iferr ! =nil {
    		fmt.Print("Error obtaining configuration file")
    		panic(err)
    	}
    }
    
    func init(a) {
    	InitConfig()
    }
    Copy the code

    Using the file introduced in main.go, initialization starts with the init function

    import(...This is not required at compile time, but it is required at run time. The mian function below cannot obtain the argument without adding this line
    	_ "gin_admin_api/common" // gin_admin_api is the module gin_admin_api configured in go.mod, usually the same as the project name...).func main(a){... port := viper.GetString("server.port")
    	fmt.Println("Current port", port)
    	...
    }
    Copy the code

InitializationgormDatabase connection tool

  • 1. Configure the database connection under Common/Database

    package common
    
    import (
    	"fmt"
    	_ "github.com/go-sql-driver/mysql"
    	"github.com/spf13/viper"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"gorm.io/gorm/logger"
    	"log"
    	"net/url"
    	"os"
    	"time"
    )
    
    var DB *gorm.DB
    
    func init(a) {
    	fmt.Println("Database Connection")
    	InitDB()
    }
    
    func InitDB(a) *gorm.DB {
    	// Get the parameters from the configuration file
    	host := viper.GetString("datasource.host")
    	port := viper.GetString("datasource.port")
    	database := viper.GetString("datasource.database")
    	username := viper.GetString("datasource.username")
    	password := viper.GetString("datasource.password")
    	charset := viper.GetString("datasource.charset")
    	loc := viper.GetString("datasource.loc")
    	// String concatenation
    	sqlStr := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s? charset=%s&parseTime=true&loc=%s",
    		username,
    		password,
    		host,
    		port,
    		database,
    		charset,
    		url.QueryEscape(loc),
    	)
    	fmt.Println("Database connection :", sqlStr)
    	// Configure log output
    	newLogger := logger.New(
    		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
    		logger.Config{
    			SlowThreshold:             time.Second,   // Cache log time
    			LogLevel:                  logger.Silent, // Log level
    			IgnoreRecordNotFoundError: true.// Ignore ErrRecordNotFound error for logger
    			Colorful:                  false.// Disable color
    		},
    	)
    	db, err := gorm.Open(mysql.Open(sqlStr), &gorm.Config{
    		Logger: newLogger,
    	})
    	iferr ! =nil {
    		fmt.Println("Failed to open database", err)
    		panic("Failed to open database" + err.Error())
    	}
    	DB = db
    	return DB
    }
    
    // TODO document address: https://gorm.io/zh_CN/docs/
    Copy the code
  • 2. Data model in model/Account.go

    package model
    
    import (
    	"gorm.io/gorm"
    )
    
    type Account struct {
    	gorm.Model
    	UserName string `gorm:"type:varchar(50); column(username); not null; unique; Comment: the account "`
    	Password string `gorm:"type:varchar(200); not null; Comment: Password "'
    	Mobile   string `gorm:"varchar(11); not null; unique; Comment: Mobile phone "'
    }
    Copy the code
  • 3. Test the created data model and database connection tool in main.go

    func init(a)  {
      // Automatically synchronize data models to data tables
    	common.DB.AutoMigrate(&model.Account{})
    }
    Copy the code
  • 4, check the database table here by default will add an S, indicating the complex number, if you want to rename the table can refer to the following code

    // In the entity class file of the data model
    
    // Customize the table name
    func (Account) TableName(a) string {
    	return "account"
    }
    Copy the code

Fifth, inginRoute group is used to implement route management

  • 1. Create a route folder that collects routes from all controllers

    package route
    
    import (
    	"gin_admin_api/controller/account"
    	"gin_admin_api/controller/login"
    	"gin_admin_api/controller/register"
    	"gin_admin_api/middleware"
    	"github.com/gin-gonic/gin"
    )
    
    func CollectRoute(router *gin.Engine) {
    	// Create an account routing group and ignore the middleware
    	accountGroup := router.Group("/account", middleware.AuthMiddleWare())
    	account.AccountRouter(accountGroup)
    	// Logged-in route
    	loginGroup := router.Group("/login")
    	login.LoginRouter(loginGroup)
     
    	registerGroup := router.Group("/register")
    	register.RegisterRouter(registerGroup)
    }
    Copy the code
  • 2. For example, the login route

    package login
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    func LoginRouter(router *gin.RouterGroup) {
    	router.POST("/", Login)
    }
    Copy the code
  • 3. Use routing groups in main.go

    func main(a) {
    	router := gin.Default()
    	// Register a routing group
    	route.CollectRoute(router)
      ...
    }
    Copy the code

Six, use data verification to achieve user registration

  • 1. Create a DTO file under the controller to receive data from the front end

    package dto
    
    import (
    	"fmt"
    	"gin_admin_api/model"
    	"github.com/go-playground/validator"
    	"unicode/utf8"
    )
    var valildate *validator.Validate
    
    func init(a) {
    	valildate = validator.New()
    	valildate.RegisterValidation("checkName", CheckNameFunc)
    }
    
    // Define the registered structure (the data structure that the front-end needs to send)
    type RegisterDto struct {
    	UserName string `validate:"required,checkName" json:"username"`
    	Password string `validate:"required" json:"password"`
    }
    
    // Customize the validator to verify the user name
    func CheckNameFunc(f validator.FieldLevel) bool {
    	count := utf8.RuneCountInString(f.Field().String())
    	if count >= 2 && count <= 12 {
    		return true
    	} else {
    		return false}}// Define a method to validate data
    func ValidatorRegister(account RegisterDto) error {
    	err := valildate.Struct(account)
    	iferr ! =nil {
    		/ / output calibration error. (the validator. ValidationErrors) is asserted
    		for _, e := range err.(validator.ValidationErrors)[:1] {
    			fmt.Println("Error field :", e.Field())
    			fmt.Println("Error value :", e.Value())
    			fmt.Println("Wrong tag:", e.Tag())
    		}
    		return err
    	} else {
    		return nil}}Copy the code
  • 2. In the controller, the data passed from the front end is inserted into the database

    // The user registers an account
    func Register(c *gin.Context) {
    	// 1. Get data from the front end
    	var registerDto dto.RegisterDto
    	err := c.Bind(&registerDto)
    	iferr ! =nil {
    		response.Fail(c, "Error parsing data passed from front end")
    		return
    	}
    	// 2. Verify the data transmitted from the front end
    	err = dto.ValidatorRegister(registerDto)
    	iferr ! =nil {
    		response.Fail(c, "Data verification error")
    		return
    	}
    	// 3. Insert data into the database
    	newPassword, err := utils.GeneratePassword(registerDto.Password)
    	iferr ! =nil {
    		response.Fail(c, "Password encryption error")
    		return
    	}
      // 4. Data structures assembled into data models
    	account := model.Account{
    		UserName: registerDto.UserName,
    		Password: newPassword,
    	}
    	tx := common.DB.Create(&account)
    	fmt.Println(tx.RowsAffected, tx.Error)
    	if tx.RowsAffected > 0 {
    		response.Success(c, nil)}else {
    		response.Fail(c, "Insert data error")}}Copy the code
  • For details on password encryption and decryption, see utils

  • 4. Check whether the database is successfully inserted

Seven, the use of middleware

  • 1. Login middleware can refer to the link address in the article
  • 2. The cross-domain middleware is relatively fixed, so we can directly use Baidu or refer to our baidu data
  • 3, log processing can refer to the document link address