欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言Gin框架前后端分離項(xiàng)目開發(fā)實(shí)例

 更新時(shí)間:2023年11月07日 09:38:54   作者:浮塵筆記  
本文主要介紹了Go語言Gin框架前后端分離項(xiàng)目開發(fā)工程化實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

基本數(shù)據(jù)配置

配置文件管理

添加依賴 go get github.com/spf13/viper,支持 JSON, TOML, YAML, HCL等格式的配置文件。在項(xiàng)目根目錄下面新建 conf 目錄,然后新建 application.yml 文件,寫入內(nèi)容如下:

server:
  port: 9988 #啟動應(yīng)用程序的端口號
datasource: #數(shù)據(jù)庫配置信息
  driverName: mysql
  host: 127.0.0.1
  port: "3306"
  database: gin_demo
  username: root
  password: rx123456
  charset: utf8
  loc: Asia/Shanghai

數(shù)據(jù)庫配置

創(chuàng)建 common/database.go 文件,使用 gorm 初始化數(shù)據(jù)庫配置:

package common

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/spf13/viper"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"net/url"
)

var DB *gorm.DB

func InitDB() *gorm.DB {
	//從配置文件中讀取數(shù)據(jù)庫配置信息
	host := viper.GetString("datasource.host")
	port := viper.Get("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")
	args := 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(args)
	db, err := gorm.Open(mysql.Open(args), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
		panic("failed to connect database, err: " + err.Error())
	}
	DB = db
	return db
}

路由配置

新建 router/routes.go 文件:

package router

import (
	"github.com/gin-gonic/gin"
	"middleware"
)

func CollectRoute(r *gin.Engine) *gin.Engine {
	r.Use(middleware.CORSMiddleware(), middleware.RecoverMiddleware())    //使用中間件
	r.POST("/api/auth/register", controller.Register)                     //注冊
	r.POST("/api/auth/login", controller.Login)                           //登錄
	r.GET("/api/auth/userinfo", middleware.AuthMiddleware(), controllers.UserDetail) //獲取詳情
	return r
}

封裝公共方法

新建 response/response.go 文件:

package response

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// 封裝的響應(yīng)體
func Response(ctx *gin.Context, httpStatus int, code int, data gin.H, msg string) {
	ctx.JSON(httpStatus, gin.H{
		"code": code,
		"data": data,
		"msg":  msg,
	})
}
func Success(ctx *gin.Context, data gin.H, msg string) {
	Response(ctx, http.StatusOK, 200, data, msg)
}
func Fail(ctx *gin.Context, data gin.H, msg string) {
	Response(ctx, http.StatusOK, 400, data, msg)
}

新建 util/util.go 文件

package util

import (
	"math/rand"
	"time"
)

// 生成隨機(jī)字符串
func RandomString(n int) string {
	var letters = []byte("asdfghjklzxcvbnmqwertyuiopASDFGHJKLZXCVBNMQWERTYUIOP")
	result := make([]byte, n)
	rand.Seed(time.Now().Unix())
	for i := range result {
		result[i] = letters[rand.Intn(len(letters))]
	}
	return string(result)
}

數(shù)據(jù)庫模型

數(shù)據(jù)表內(nèi)容

CREATE TABLE `user_infos` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL DEFAULT '',
  `telephone` varchar(11) NOT NULL DEFAULT '',
  `password` varchar(255) NOT NULL DEFAULT '',
  `created_at` datetime(3) DEFAULT NULL,
  `updated_at` datetime(3) DEFAULT NULL,
  `deleted_at` datetime(3) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

model文件

新建 model/User.go 文件:

package model

import "gorm.io/gorm"

type UserInfo struct {
	gorm.Model        //繼承g(shù)orm的Model,里面包含了ID、CreatedAt、UpdatedAt、DeletedAt
	Name       string `gorm:"type:varchar(20);not null"`
	Telephone  string `gorm:"varchar(11);not null;unique"`
	Password   string `gorm:"size:255;not null"`
}

DTO文件

DTO就是數(shù)據(jù)傳輸對象(Data Transfer Object)的縮寫;用于展示層與服務(wù)層之間的數(shù)據(jù)傳輸對象。
新建 response/user_dto.go文件:

package response

import (
	model2 "gin-demo/model"
)

type UserDto struct {
	Name      string `json:"name"`
	Telephone string `json:"telephone"`
}

// DTO就是數(shù)據(jù)傳輸對象(Data Transfer Object)的縮寫;用于 展示層與服務(wù)層之間的數(shù)據(jù)傳輸對象
func ToUserDto(user model2.UserInfo) UserDto {
	return UserDto{
		Name:      user.Name,
		Telephone: user.Telephone,
	}
}

中間件

錯(cuò)誤異常捕獲中間件

新建 middleware/RecoveryMiddleware.go 文件:

package middleware

import (
	"fmt"
	response2 "gin-demo/response"
	"github.com/gin-gonic/gin"
)

func RecoverMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				response2.Fail(c, nil, fmt.Sprint(err))
				c.Abort()
				return
			}
		}()
	}
}

跨域中間件

跨域,指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript施加的安全限制。新建 middleware/CORSMiddleware.go 文件:

package middleware

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// 跨域中間件
func CORSMiddleware() gin.HandlerFunc { //CORS是跨源資源分享(Cross-Origin Resource Sharing)中間件
	return func(ctx *gin.Context) {
		//指定允許其他域名訪問
		//ctx.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:8080")
		ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*") //跨域:CORS(跨來源資源共享)策略
		//預(yù)檢結(jié)果緩存時(shí)間
		ctx.Writer.Header().Set("Access-Control-Max-Age", "86400")
		//允許的請求類型(GET,POST等)
		ctx.Writer.Header().Set("Access-Control-Allow-Methods", "*")
		//允許的請求頭字段
		ctx.Writer.Header().Set("Access-Control-Allow-Headers", "*")
		//是否允許后續(xù)請求攜帶認(rèn)證信息(cookies),該值只能是true,否則不返回
		ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		if ctx.Request.Method == http.MethodOptions {
			ctx.AbortWithStatus(200)
		} else {
			ctx.Next()
		}
	}
}

token認(rèn)證中間件

新建 middleware/AuthMiddleware.go 文件:

package middleware

import (
	common2 "gin-demo/common"
	model2 "gin-demo/model"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
)

// token認(rèn)證中間件(權(quán)限控制)
func AuthMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		auth := "jiangzhou"
		// 獲取authorization header
		tokenString := ctx.GetHeader("Authorization") //postman測試:在Headers中添加: key:Authorization;value:jiangzhou:xxx(token值)
		//fmt.Println(tokenString)
		//fmt.Println(strings.HasPrefix(tokenString,auth+""))
		// 無效的token
		//if tokenString == "" || !strings.HasPrefix(tokenString, "Bearer ") { //驗(yàn)證token的前綴為:
		if tokenString == "" || !strings.HasPrefix(tokenString, auth+":") { //驗(yàn)證token的前綴為:
			ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "權(quán)限不足"})
			ctx.Abort()
			return
		}
		index := strings.Index(tokenString, auth+":") //找到token前綴對應(yīng)的位置
		tokenString = tokenString[index+len(auth)+1:] //截取真實(shí)的token(開始位置為:索引開始的位置+關(guān)鍵字符的長度+1(:的長度為1))
		//fmt.Println("截取之后的數(shù)據(jù):",tokenString)
		token, claims, err := common2.ParseToken(tokenString)
		if err != nil || !token.Valid { //解析錯(cuò)誤或者過期等
			ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "權(quán)限不足"})
			ctx.Abort()
			return
		}

		// 驗(yàn)證通過后獲取claim 中的userId
		userId := claims.UserId
		//判定
		var user model2.UserInfo
		common2.DB.First(&user, userId)
		if user.ID == 0 { //如果沒有讀取到內(nèi)容,說明token值有誤
			ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "權(quán)限不足"})
			ctx.Abort()
			return
		}
		ctx.Set("user", user) //將key-value值存儲到context中
		ctx.Next()

	}
}

JWT

新建 common/jwt.go 文件:

package common

import (
	model2 "gin-demo/model"
	"github.com/dgrijalva/jwt-go"
	"time"
)

var jwtKey = []byte("a_secret_key") //證書簽名秘鑰(該秘鑰非常重要,如果client端有該秘鑰,就可以簽發(fā)證書了)

type Claims struct {
	UserId uint
	jwt.StandardClaims
}

// 分發(fā)證書
func ReleaseToken(user model2.UserInfo) (string, error) {
	expirationTime := time.Now().Add(7 * 24 * time.Hour) //截止時(shí)間:從當(dāng)前時(shí)刻算起,7天
	claims := &Claims{
		UserId: user.ID,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(), //過期時(shí)間
			IssuedAt:  time.Now().Unix(),     //發(fā)布時(shí)間
			Issuer:    "jiangzhou",           //發(fā)布者
			Subject:   "user token",          //主題
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) //生成token
	tokenString, err := token.SignedString(jwtKey)             //簽名

	if err != nil {
		return "", err
	}

	return tokenString, nil
}

// 解析證書
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
	claims := &Claims{}
	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (i interface{}, err error) {
		return jwtKey, nil
	})
	return token, claims, err
}

控制器

UserController

新建 controllers/UserController.go 文件:

package controllers

import (
	"fmt"
	common2 "gin-demo/common"
	model2 "gin-demo/model"
	response2 "gin-demo/response"
	util2 "gin-demo/util"
	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"net/http"
)

// 注冊
func UserRegister(ctx *gin.Context) {
	var requestUser model2.UserInfo
	ctx.Bind(&requestUser)
	name := requestUser.Name
	telephone := requestUser.Telephone
	password := requestUser.Password
	// 數(shù)據(jù)驗(yàn)證
	if len(telephone) != 11 {
		//422 Unprocessable Entity 無法處理的請求實(shí)體
		response2.Response(ctx, http.StatusUnprocessableEntity, 422, nil, "手機(jī)號必須為11位")
		fmt.Println(telephone, len(telephone))
		return
	}
	if len(password) < 6 {
		response2.Response(ctx, http.StatusUnprocessableEntity, 422, nil, "密碼不能少于6位")
		return
	}
	// 如果名稱沒有傳,給一個(gè)10位的隨機(jī)字符串
	if len(name) == 0 {
		name = util2.RandomString(10)
	}
	// 判斷手機(jī)號是否存在
	if isTelephoneExist(common2.DB, telephone) {
		response2.Response(ctx, http.StatusUnprocessableEntity, 422, nil, "用戶已經(jīng)存在")
		return
	}
	// 創(chuàng)建用戶
	//返回密碼的hash值(對用戶密碼進(jìn)行二次處理,防止系統(tǒng)管理人員利用)
	hashPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		response2.Response(ctx, http.StatusInternalServerError, 500, nil, "加密錯(cuò)誤")
		return
	}
	newUser := model2.UserInfo{
		Name:      name,
		Telephone: telephone,
		Password:  string(hashPassword),
	}
	common2.DB.Create(&newUser) // 新增記錄
	// 發(fā)放token
	token, err := common2.ReleaseToken(newUser)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "系統(tǒng)異常"})
		return
	}
	// 返回結(jié)果
	response2.Success(ctx, gin.H{"token": token}, "注冊成功")
}
func UserLogin(ctx *gin.Context) {
	var requestUser model2.UserInfo
	ctx.Bind(&requestUser)
	//name := requestUser.Name
	telephone := requestUser.Telephone
	password := requestUser.Password
	// 數(shù)據(jù)驗(yàn)證
	if len(telephone) != 11 {
		//422 Unprocessable Entity 無法處理的請求實(shí)體
		response2.Response(ctx, http.StatusUnprocessableEntity, 422, nil, "手機(jī)號必須為11位")
		fmt.Println(telephone, len(telephone))
		return
	}
	if len(password) < 6 {
		response2.Response(ctx, http.StatusUnprocessableEntity, 422, nil, "密碼不能少于6位")
		return
	}
	// 依據(jù)手機(jī)號,查詢用戶注冊的數(shù)據(jù)記錄
	var user model2.UserInfo
	common2.DB.Where("telephone=?", telephone).First(&user)
	if user.ID == 0 {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"code": 422, "msg": "用戶不存在"})
		return
	}
	// 判斷密碼收否正確
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "密碼錯(cuò)誤"})
		return
	}
	// 發(fā)放token
	token, err := common2.ReleaseToken(user)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"code": 500, "msg": "系統(tǒng)異常"})
		return
	}
	// 返回結(jié)果
	response2.Success(ctx, gin.H{"token": token}, "登錄成功")

}
func UserDetail(ctx *gin.Context) {
	user, _ := ctx.Get("user")
	response2.Success(ctx, gin.H{
		"user": response2.ToUserDto(user.(model2.UserInfo))}, "響應(yīng)成功")
}
func isTelephoneExist(db *gorm.DB, telephone string) bool {
	var user model2.UserInfo
	db.Where("telephone=?", telephone).First(&user)
	//如果沒有查詢到數(shù)據(jù),對于uint數(shù)據(jù),默認(rèn)值為:0
	if user.ID != 0 {
		return true
	}
	return false
}

運(yùn)行調(diào)試

注冊接口

在這里插入圖片描述

在這里插入圖片描述

登錄接口

在這里插入圖片描述

獲取用戶信息

在header中傳遞token數(shù)據(jù)

在這里插入圖片描述

構(gòu)建發(fā)布項(xiàng)目

在項(xiàng)目根目錄下執(zhí)行 go build,然后會生成 gin-demo 的文件,然后可以將這個(gè)二進(jìn)制文件拷貝到任意目錄下,另外需要將項(xiàng)目下面的 conf 目錄也拷貝過去。

在這里插入圖片描述

然后執(zhí)行 ./gin-demo 即可運(yùn)行服務(wù):

在這里插入圖片描述

以上代碼參考:https://gitee.com/rxbook/gin-demo

前端VUE調(diào)用接口

準(zhǔn)備了一個(gè)簡單的前端頁面,代碼在https://gitee.com/rxbook/vue-demo1 ,本地運(yùn)行:

#安裝依賴
npm install 
#運(yùn)行
npm run serve

發(fā)布構(gòu)建:

npm run build

構(gòu)建完成后會生成 dist 目錄,然后在nginx中配置虛擬主機(jī):

server {
        listen          80;
        server_name     vue-demo1.cc;
        root    /home/rx/web_front/vue-demo1/dist;

        location ^~ /api/ {
                proxy_pass http://127.0.0.1:9988;
        }
}

配置 /etc/hosts 后,在瀏覽器訪問:

在這里插入圖片描述

到此這篇關(guān)于Go語言Gin框架前后端分離項(xiàng)目開發(fā)實(shí)例的文章就介紹到這了,更多相關(guān)Gin框架前后端分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論