A,gin
Project 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
Initializationgorm
Database 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, ingin
Route 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(®isterDto) 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