golang gin web开发

源码地址:https://gitee.com/LearningContainer/gin-web.git

下载Gin

image.png
修改镜像
image.png

创建项目

mkdir go-web
cd go-web
# 初始化项目
go mod init go-web
# 创建gin
go get -u github.com/gin-gonic/gin

创建成功目录下会有一个go.sum文件管理依赖的

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	r.GET("hello", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "Hello world")
	})

	r.Run(":9999")
}

# 启动项目
go run main.go

image.png


image.png

路由组

package router

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("user")
	{
		user.GET("hello", func(ctx *gin.Context) {
			ctx.String(http.StatusOK, "Hello world")
		})
	}

	return r
}

package main

import (
	"go-web/router"
)

func main() {
	r := router.Router()
	r.Run(":9999")
}

响应统一封装

package controllers

import (
	"crypto/md5"
	"encoding/hex"
	"github.com/gin-gonic/gin"
)

type JsonStruct struct {
	Code  int         `json:"code"`
	Msg   interface{} `json:"msg"`
	Data  interface{} `json:"data"`
	Count int64       `json:"count"`
}

func ReturnSuccess(c *gin.Context, code int, msg interface{}, data interface{}, count int64) {
	json := &JsonStruct{Code: code, Msg: msg, Data: data, Count: count}
	c.JSON(200, json)
}

func ReturnError(c *gin.Context, code int, msg interface{}) {
	json := &JsonStruct{Code: code, Msg: msg}
	c.JSON(200, json)
}

func EncryMd5(s string) string {
	ctx := md5.New()
	ctx.Write([]byte(s))
	return hex.EncodeToString(ctx.Sum(nil))
}

请求参数body

package controllers

import (
	"fmt"

	"github.com/gin-gonic/gin"
)

type LoginRequest struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type UserController struct{}

func (u UserController) Login(c *gin.Context) {
	var loginRequest LoginRequest

	// 使用 ShouldBindJSON 绑定请求体数据到结构体
	if err := c.ShouldBindJSON(&loginRequest); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	// 打印传递过来的值
	fmt.Println("Username:", loginRequest.Username)
	fmt.Println("Password:", loginRequest.Password)
	ReturnSuccess(c, 0, "success", "请求成功", 1)
}

package router

import (
	"go-web/controllers"

	"github.com/gin-gonic/gin"
)

func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("/user")
	{
		user.POST("/login", controllers.UserController{}.Login)
	}
	return r
}

测试一下,要求从body传递两个参数
image.png

URL携带参数

/path

func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("/user")
	{
		user.POST("/login", controllers.UserController{}.Login)
		user.GET("/info/:id", controllers.UserController{}.UserInfo)
	}
	return r
}
func (u UserController) UserInfo(c *gin.Context) {
	// 获取传递过来的参数
	id := c.Param("id")
	ReturnSuccess(c, 0, "success", id, 1)
}

image.png

url&
func (u UserController) UserList(c *gin.Context) {
	// 获取传递过来的参数
	id := c.Query("id")
	name := c.DefaultQuery("name", "杨胖胖")
	ReturnSuccess(c, 0, name, id, 1)
}
func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("/user")
	{
		user.POST("/login", controllers.UserController{}.Login)
		user.GET("/info/:id", controllers.UserController{}.UserInfo)
		user.GET("/list", controllers.UserController{}.UserList)
	}
	return r
}
默认参数

image.png

全参数

image.png
如果是post请求时就需要换成PostForm

func (u UserController) UserList(c *gin.Context) {
	// 获取传递过来的参数
	id := c.PostForm("id")
	name := c.DefaultPostForm("name", "杨胖胖")
	ReturnSuccess(c, 0, name, id, 1)
}

异常捕获

func Router() *gin.Engine {
	r := gin.Default()

	user := r.Group("/user")
	{
		user.POST("/login", controllers.UserController{}.Login)
		user.GET("/info/:id", controllers.UserController{}.UserInfo)
		user.GET("/list", controllers.UserController{}.UserList)
		user.GET("/error", controllers.UserController{}.UserError)
	}
	return r
}
func (u UserController) UserError(c *gin.Context) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("捕获异常:", err)
		}
	}()

	ReturnError(c, 500, 0)
}

全局异常处理

package exception

import (
	"fmt"
	"go-web/controllers"
	"net/http"

	"github.com/gin-gonic/gin"
)

func GlobalExceptionHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// 在这里处理异常
				fmt.Println("捕获异常:", err)
				// 返回自定义的错误响应
				controllers.ReturnError(c, http.StatusInternalServerError, "Internal Server Error")
				// 中止后续处理
				c.Abort()
			}
		}()
		c.Next()
	}
}

使用中间件的方式注册到router.go
r.Use(exception.GlobalExceptionHandler())

func Router() *gin.Engine {
	r := gin.Default()

	// 应用全局异常处理器
	r.Use(exception.GlobalExceptionHandler())
    
	r.Use(gin.LoggerWithConfig(logger.LoggerToFile()))
	r.Use(logger.Recover)

	user := r.Group("/user")
	{
		user.POST("/login", controllers.UserController{}.Login)
		user.GET("/info/:id", controllers.UserController{}.UserInfo)
		user.GET("/list", controllers.UserController{}.UserList)
		user.GET("/error", controllers.UserController{}.UserError)
	}
	return r
}

这个时候就不需要局部去处理异常了

func (u UserController) UserError(c *gin.Context) {
	num1 := 1
	num2 := 0
	num3 := num1 / num2

	ReturnError(c, 500, num3)
}

返回的也是自定义的异常
image.png

日志

go get -u github.com/sirupsen/logrus
package logger

import (
	"github.com/gin-gonic/gin"
    "fmt"
    "github.com/sirupsen/logrus"
    "io"
	"os"
    "path"
	"time"
    "runtime/debug"
    "net/http"
)

func init()  {
    // 设置日志格式为json格式
    logrus.SetFormatter(&logrus.JSONFormatter{
        TimestampFormat: "2006-01-02 15:04:05",
    })
    logrus.SetReportCaller(false)
}

func Write(msg string, filename string) {
	setOutPutFile(logrus.InfoLevel, filename)
    logrus.Info(msg)
}

func Debug(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.DebugLevel, "debug")
    logrus.WithFields(fields).Debug(args)
}

func Info(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.InfoLevel, "info")
    logrus.WithFields(fields).Info(args)
}

func Warn(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.WarnLevel, "warn")
    logrus.WithFields(fields).Warn(args)
}

func Fatal(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.FatalLevel, "fatal")
    logrus.WithFields(fields).Fatal(args)
}

func Error(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.ErrorLevel, "error")
    logrus.WithFields(fields).Error(args)
}

func Panic(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.PanicLevel, "panic")
    logrus.WithFields(fields).Panic(args)
}

func Trace(fields logrus.Fields, args ...interface{})  {
    setOutPutFile(logrus.TraceLevel, "trace")
    logrus.WithFields(fields).Trace(args)
}

func setOutPutFile(level logrus.Level, logName string) {
    if _, err := os.Stat("./runtime/log"); os.IsNotExist(err) {
        err = os.MkdirAll("./runtime/log", 0777)
        if err != nil {
            panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", err))
        }
    }

	timeStr:=time.Now().Format("2006-01-02")
    fileName := path.Join("./runtime/log", logName + "_" + timeStr + ".log")


    var err error
    os.Stderr, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
    if err != nil {
        fmt.Println("open log file err", err)
    }
    logrus.SetOutput(os.Stderr)
    logrus.SetLevel(level)
    return
}

func LoggerToFile() gin.LoggerConfig {

	if _, err := os.Stat("./runtime/log"); os.IsNotExist(err) {
        err = os.MkdirAll("./runtime/log", 0777)
        if err != nil {
            panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", err))
        }
    }

	timeStr:=time.Now().Format("2006-01-02")
    fileName := path.Join("./runtime/log", "success_" + timeStr + ".log")


    os.Stderr, _ = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)

	var conf = gin.LoggerConfig{
		Formatter:  func(param gin.LogFormatterParams) string{
			return fmt.Sprintf("%s - %s \"%s %s %s %d %s \"%s\" %s\"\n",
				param.TimeStamp.Format("2006-01-02 15:04:05"),
				param.ClientIP,
				param.Method,
				param.Path,
				param.Request.Proto,
				param.StatusCode,
				param.Latency,
				param.Request.UserAgent(),
				param.ErrorMessage,
			)
		},
		Output: io.MultiWriter(os.Stdout, os.Stderr),
	}


	return conf
}

func Recover(c *gin.Context){
	defer func() {
		if err := recover(); err != nil {
            if _, errDir := os.Stat("./runtime/log"); os.IsNotExist(errDir) {
                errDir = os.MkdirAll("./runtime/log", 0777)
                if errDir != nil {
                    panic(fmt.Errorf("create log dir '%s' error: %s", "./runtime/log", errDir))
                }
            }
        
            timeStr:=time.Now().Format("2006-01-02")
            fileName := path.Join("./runtime/log", "error_" + timeStr + ".log")
        
        
            f, errFile := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
            if errFile != nil {
                fmt.Println(errFile)
            }
            timeFileStr := time.Now().Format("2006-01-02 15:04:05")
            f.WriteString("panic error time:" + timeFileStr + "\n")
            f.WriteString(fmt.Sprintf("%v", err) + "\n")
            f.WriteString("stacktrace from panic:" + string(debug.Stack()) + "\n")
            f.Close()
            c.JSON(http.StatusOK, gin.H{
				"code": 500,
				"msg":  fmt.Sprintf("%v", err),
			})
			//终止后续接口调用,不加的话recover到异常后,还会继续执行接口里后续代码
			c.Abort()
		}
	}()
	c.Next()
}

通过中间件的方式调用的日志

func Router() *gin.Engine {
	r := gin.Default()
    // 日志
	r.Use(gin.LoggerWithConfig(logger.LoggerToFile()))
	r.Use(logger.Recover)
	user := r.Group("/user")
	{
		user.POST("/login", controllers.UserController{}.Login)
		user.GET("/info/:id", controllers.UserController{}.UserInfo)
		user.GET("/list", controllers.UserController{}.UserList)
		user.GET("/error", controllers.UserController{}.UserError)
	}
	return r
}

发起访问后就会生成日志信息
image.png


image.png

Gorm(数据库连接)

go get -u github.com/jinzhu/gorm/dialects/mysql
go get -u gorm.io/gorm
package config

const (
	Mysqldb = "root:123456@tcp(127.0.0.1:3301)/go?charset=utf8"
)

package dao

import (
	"go-web/config"
	"go-web/pkg/logger"
	"time"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var (
	Db  *gorm.DB
	err error
)

func init() {
	Db, err = gorm.Open("mysql", config.Mysqldb)
	if err != nil {
		logger.Error(map[string]interface{}{"mysql connect error": err.Error()})
	}
	if Db.Error != nil {
		logger.Error(map[string]interface{}{"database error": Db.Error})
	}

	// 启用 GORM 的日志记录器
	Db.LogMode(true)

	// ----------------------- 连接池设置 -----------------------
	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
	Db.DB().SetMaxIdleConns(10)

	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	Db.DB().SetMaxOpenConns(100)

	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	Db.DB().SetConnMaxLifetime(time.Hour)
}

package models

import (
	"go-web/dao"
	"time"
)

type User struct {
	ID          uint      `json:"id"`
	Name        string    `json:"name"`
	Email       string    `json:"email"`
	Age         uint8     `json:"age,omitempty"`
	Birthday    time.Time `json:"birthday,omitempty"`
	MemberNum   string    `json:"member_number,omitempty"`
	ActivatedAt time.Time `json:"activated_at,omitempty"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

func (User) TableName() string {
	return "users"
}

func GetUser(id int) (User, error) {
	var user User
	err := dao.Db.Where("id = ?", id).First(&user).Error
	return user, err
}


func (u UserController) UserList(c *gin.Context) {
	// 获取传递过来的参数
	idStr := c.Query("id")
	name := c.DefaultQuery("name", "杨胖胖")
	// 转成int
	id, _ := strconv.Atoi(idStr)
	user, _ := models.GetUser(id)
	ReturnSuccess(c, 0, name, user, 1)
}

目前可以先忽略这个日期格式转换失败的问题
image.png因为添加了sql日志打印,这里可以看到sql详情
image.png

  • 11
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值