最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang 經(jīng)典校驗(yàn)庫(kù) validator 用法解析

 更新時(shí)間:2022年08月26日 09:45:09   作者:ag9920  
這篇文章主要為大家介紹了Golang 經(jīng)典校驗(yàn)庫(kù) validator 用法解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

開(kāi)篇

今天繼續(xù)我們的 Golang 經(jīng)典開(kāi)源庫(kù)學(xué)習(xí)之旅,這篇文章的主角是 validator,Golang 中經(jīng)典的校驗(yàn)庫(kù),它可以讓開(kāi)發(fā)者可以很便捷地通過(guò) tag 來(lái)控制對(duì)結(jié)構(gòu)體字段的校驗(yàn),使用面非常廣泛。

本來(lái)打算一節(jié)收尾,越寫(xiě)越發(fā)現(xiàn) validator 整體復(fù)雜度還是很高的,而且支持了很多場(chǎng)景??刹鸾獾乃悸泛芏啵谑谴蛩惴殖蓛善恼聛?lái)講。這篇我們會(huì)先來(lái)了解 validator 的用法,下一篇我們會(huì)關(guān)注實(shí)現(xiàn)的思路和源碼解析。

validator

Package validator implements value validations for structs and individual fields based on tags.

validator 是一個(gè)結(jié)構(gòu)體參數(shù)驗(yàn)證器。

它提供了【基于 tag 對(duì)結(jié)構(gòu)體以及單獨(dú)屬性的校驗(yàn)?zāi)芰Α?。?jīng)典的 gin 框架就是用了 validator 作為默認(rèn)的校驗(yàn)器。它的能力能夠幫助開(kāi)發(fā)者最大程度地減少【基礎(chǔ)校驗(yàn)】的代碼,你只需要一個(gè) tag 就能完成校驗(yàn)。完整的文檔參照 這里。

目前 validator 最新版本已經(jīng)升級(jí)到了 v10,我們可以用

go get github.com/go-playground/validator/v10

添加依賴(lài)后,import 進(jìn)來(lái)即可

import "github.com/go-playground/validator/v10"

我們先來(lái)看一個(gè)簡(jiǎn)單的例子,了解 validator 能怎樣幫助開(kāi)發(fā)者完成校驗(yàn)。

package main
import (
	"fmt"
	"github.com/go-playground/validator/v10"
)
type User struct {
	Name string `validate:"min=6,max=10"`
	Age  int    `validate:"min=1,max=100"`
}
func main() {
	validate := validator.New()
	u1 := User{Name: "lidajun", Age: 18}
	err := validate.Struct(u1)
	fmt.Println(err)
	u2 := User{Name: "dj", Age: 101}
	err = validate.Struct(u2)
	fmt.Println(err)
}

這里我們有一個(gè) User 結(jié)構(gòu)體,我們希望 Name 這個(gè)字符串長(zhǎng)度在 [6, 10] 這個(gè)區(qū)間內(nèi),并且希望 Age 這個(gè)數(shù)字在 [1, 100] 區(qū)間內(nèi)。就可以用上面這個(gè) tag。

校驗(yàn)的時(shí)候只需要三步:

  • 調(diào)用 validator.New() 初始化一個(gè)校驗(yàn)器;
  • 將【待校驗(yàn)的結(jié)構(gòu)體】傳入我們的校驗(yàn)器的 Struct 方法中;
  • 校驗(yàn)返回的 error 是否為 nil 即可。

上面的例子中,lidajun 長(zhǎng)度符合預(yù)期,18 這個(gè) Age 也在區(qū)間內(nèi),預(yù)期 err 為 nil。而第二個(gè)用例 Name 和 Age 都在區(qū)間外。我們運(yùn)行一下看看結(jié)果:

<nil>
Key: 'User.Name' Error:Field validation for 'Name' failed on the 'min' tag
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'max' tag

這里我們也可以看到,validator 返回的報(bào)錯(cuò)信息包含了 Field 名稱(chēng) 以及 tag 名稱(chēng),這樣我們也容易判斷哪個(gè)校驗(yàn)沒(méi)過(guò)。

如果沒(méi)有 tag,我們自己手寫(xiě)的話(huà),還需要這樣處理:

func validate(u User) bool {
	if u.Age < 1 || u.Age > 100 {
		return false
	}
	if len(u.Name) < 6 || len(u.Name) > 10 {
		return false
	}
	return true
}

乍一看好像區(qū)別不大,其實(shí)一旦結(jié)構(gòu)體屬性變多,校驗(yàn)規(guī)則變復(fù)雜,這個(gè)校驗(yàn)函數(shù)的代價(jià)立刻會(huì)上升,另外你還要顯示的處理報(bào)錯(cuò)信息,以達(dá)到上面這樣清晰的效果(這個(gè)手寫(xiě)的示例代碼只返回了一個(gè) bool,不好判斷是哪個(gè)沒(méi)過(guò))。

越是大結(jié)構(gòu)體,越是規(guī)則復(fù)雜,validator 的收益就越高。我們還可以把 validator 放到中間件里面,對(duì)所有請(qǐng)求加上校驗(yàn),用的越多,效果越明顯。

其實(shí)筆者個(gè)人使用經(jīng)驗(yàn)來(lái)看,validator 帶來(lái)的另外兩個(gè)好處在于:

  • 因?yàn)樾枰?jīng)常使用校驗(yàn)?zāi)芰?,養(yǎng)成了習(xí)慣,每定義一個(gè)結(jié)構(gòu),都事先想好每個(gè)屬性應(yīng)該有哪些約束,促使開(kāi)發(fā)者思考自己的模型。這一點(diǎn)非常重要,很多時(shí)候我們就是太隨意定義一些結(jié)構(gòu),沒(méi)有對(duì)應(yīng)的校驗(yàn),結(jié)果導(dǎo)致各種臟數(shù)據(jù),把校驗(yàn)邏輯一路下沉;
  • 有了 tag 來(lái)描述約束規(guī)則,讓結(jié)構(gòu)體本身更容易理解,可讀性,可維護(hù)性提高。一看結(jié)構(gòu)體,掃幾眼 tag 就知道業(yè)務(wù)對(duì)它的預(yù)期。

這兩個(gè)點(diǎn)雖然比較【意識(shí)流】,但在開(kāi)發(fā)習(xí)慣上還是很重要的。

好了,到目前只是淺嘗輒止,下面我們結(jié)合示例看看 validator 到底提供了哪些能力。

使用方法

我們上一節(jié)舉的例子就是最簡(jiǎn)單的場(chǎng)景,在一個(gè) struct 中定義好 validate:"xxx" tag,然后調(diào)用校驗(yàn)器的 err := validate.Struct(user) 方法來(lái)校驗(yàn)。

這一節(jié)我們結(jié)合實(shí)例來(lái)看看最常用的場(chǎng)景下,我們會(huì)怎樣用 validator:

package main
import (
	"fmt"
	"github.com/go-playground/validator/v10"
)
// User contains user information
type User struct {
	FirstName      string     `validate:"required"`
	LastName       string     `validate:"required"`
	Age            uint8      `validate:"gte=0,lte=130"`
	Email          string     `validate:"required,email"`
	FavouriteColor string     `validate:"iscolor"`                // alias for 'hexcolor|rgb|rgba|hsl|hsla'
	Addresses      []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
	Street string `validate:"required"`
	City   string `validate:"required"`
	Planet string `validate:"required"`
	Phone  string `validate:"required"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
	validate = validator.New()
	validateStruct()
	validateVariable()
}
func validateStruct() {
	address := &Address{
		Street: "Eavesdown Docks",
		Planet: "Persphone",
		Phone:  "none",
	}
	user := &User{
		FirstName:      "Badger",
		LastName:       "Smith",
		Age:            135,
		Email:          "Badger.Smith@gmail.com",
		FavouriteColor: "#000-",
		Addresses:      []*Address{address},
	}
	// returns nil or ValidationErrors ( []FieldError )
	err := validate.Struct(user)
	if err != nil {
		// this check is only needed when your code could produce
		// an invalid value for validation such as interface with nil
		// value most including myself do not usually have code like this.
		if _, ok := err.(*validator.InvalidValidationError); ok {
			fmt.Println(err)
			return
		}
		for _, err := range err.(validator.ValidationErrors) {
			fmt.Println(err.Namespace())
			fmt.Println(err.Field())
			fmt.Println(err.StructNamespace())
			fmt.Println(err.StructField())
			fmt.Println(err.Tag())
			fmt.Println(err.ActualTag())
			fmt.Println(err.Kind())
			fmt.Println(err.Type())
			fmt.Println(err.Value())
			fmt.Println(err.Param())
			fmt.Println()
		}
		// from here you can create your own error messages in whatever language you wish
		return
	}
	// save user to database
}
func validateVariable() {
	myEmail := "joeybloggs.gmail.com"
	errs := validate.Var(myEmail, "required,email")
	if errs != nil {
		fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag
		return
	}
	// email ok, move on
}

仔細(xì)觀察你會(huì)發(fā)現(xiàn),第一步永遠(yuǎn)是創(chuàng)建一個(gè)校驗(yàn)器,一個(gè) validator.New() 解決問(wèn)題,后續(xù)一定要復(fù)用,內(nèi)部有緩存機(jī)制,效率比較高。

關(guān)鍵在第二步,大體上分為兩類(lèi):

  • 基于結(jié)構(gòu)體調(diào)用 err := validate.Struct(user) 來(lái)校驗(yàn);
  • 基于變量調(diào)用 errs := validate.Var(myEmail, "required,email")

結(jié)構(gòu)體校驗(yàn)這個(gè)相信看完這個(gè)實(shí)例,大家已經(jīng)很熟悉了。

變量校驗(yàn)這里很有意思,用起來(lái)確實(shí)簡(jiǎn)單,大家看 validateVariable 這個(gè)示例就 ok,但是,但是,我只有一個(gè)變量,我為啥還要用這個(gè) validator ???

原因很簡(jiǎn)單,不要以為 validator 只能干一些及其簡(jiǎn)單的,比大小,比長(zhǎng)度,判空邏輯。這些非常基礎(chǔ)的校驗(yàn)用一個(gè) if 語(yǔ)句也搞定。

validator 支持的校驗(yàn)規(guī)則遠(yuǎn)比這些豐富的多。

我們先把前面示例的結(jié)構(gòu)體拿出來(lái),看看支持哪些 tag:

// User contains user information
type User struct {
	FirstName      string     `validate:"required"`
	LastName       string     `validate:"required"`
	Age            uint8      `validate:"gte=0,lte=130"`
	Email          string     `validate:"required,email"`
	FavouriteColor string     `validate:"iscolor"`                // alias for 'hexcolor|rgb|rgba|hsl|hsla'
	Addresses      []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
// Address houses a users address information
type Address struct {
	Street string `validate:"required"`
	City   string `validate:"required"`
	Planet string `validate:"required"`
	Phone  string `validate:"required"`
}

格式都是 validate:"xxx",這里不再說(shuō),關(guān)鍵是里面的配置。

validator 中如果你針對(duì)同一個(gè) Field,有多個(gè)校驗(yàn)項(xiàng),可以用下面兩種運(yùn)算符:

  • , 逗號(hào)表示【與】,即每一個(gè)都需要滿(mǎn)足;
  • | 表示【或】,多個(gè)條件滿(mǎn)足一個(gè)即可。

我們一個(gè)個(gè)來(lái)看這個(gè) User 結(jié)構(gòu)體出現(xiàn)的 tag:

  • required 要求必須有值,不為空;
  • gte=0,lte=130 其中 gte 代表大于等于,lte 代表小于等于,這個(gè)語(yǔ)義是 [0,130] 區(qū)間;
  • required, emal 不僅僅要有值,還得符合 Email 格式;
  • iscolor 后面注釋也提了,這是個(gè)別名,本質(zhì)等價(jià)于 hexcolor|rgb|rgba|hsl|hsla,屬于 validator 自帶的別名能力,符合這幾個(gè)規(guī)則任一的,我們都認(rèn)為屬于表示顏色。
  • required,dive,required 這個(gè) dive 大有來(lái)頭,注意這個(gè) Addresses 是個(gè) Address 數(shù)組,我們加 tag 一般只是針對(duì)單獨(dú)的數(shù)據(jù)類(lèi)型,這種【容器型】的怎么辦?

這時(shí) dive 的能力就派上用場(chǎng)了。

dive 的語(yǔ)義在于告訴 validator 不要停留在我這一級(jí),而是繼續(xù)往下校驗(yàn),無(wú)論是 slice, array 還是 map,校驗(yàn)要用的 tag 就是在 dive 之后的這個(gè)。

這樣說(shuō)可能不直觀,我們來(lái)看一個(gè)例子:

[][]string with validation tag "gt=0,dive,len=1,dive,required"
// gt=0 will be applied to []
// len=1 will be applied to []string
// required will be applied to string

第一個(gè) gt=0 適用于最外層的數(shù)組,出現(xiàn) dive 后,往下走,len=1 作為一個(gè) tag 適用于內(nèi)層的 []string,此后又出現(xiàn) dive,繼續(xù)往下走,對(duì)于最內(nèi)層的每個(gè) string,要求每個(gè)都是 required。

[][]string with validation tag "gt=0,dive,dive,required"
// gt=0 will be applied to []
// []string will be spared validation
// required will be applied to string

第二個(gè)例子,看看能不能理解?

其實(shí),只要記住,每次出現(xiàn) dive,都往里面走就 ok。

回到我們一開(kāi)始的例子:

Addresses []*Address validate:"required,dive,required"

表示的意思是,我們要求 Addresses 這個(gè)數(shù)組是 required,此外對(duì)于每個(gè)元素,也得是 required。

內(nèi)置校驗(yàn)器

validator 對(duì)于下面六種場(chǎng)景都提供了豐富的校驗(yàn)器,放到 tag 里就能用。這里我們簡(jiǎn)單看一下:

(注:想看完整的建議參考文檔 以及倉(cāng)庫(kù) README

1. Fields

對(duì)于結(jié)構(gòu)體各個(gè)屬性的校驗(yàn),這里可以針對(duì)一個(gè) field 與另一個(gè) field 相互比較。

2. Network

網(wǎng)絡(luò)相關(guān)的格式校驗(yàn),可以用來(lái)校驗(yàn) IP 格式,TCP, UDP, URL 等

3. Strings

字符串相關(guān)的校驗(yàn),用的非常多,比如校驗(yàn)是否是數(shù)字,大小寫(xiě),前后綴等,非常方便。

4. Formats

符合特定格式,如我們上面提到的 email,信用卡號(hào),顏色,html,base64,json,經(jīng)緯度,md5 等

5. Comparisons

比較大小,用的很多

6. Other

雜項(xiàng),各種通用能力,用的也非常多,我們上面用的 required 就在這一節(jié)。包括校驗(yàn)是否為默認(rèn)值,最大,最小等。

7. 別名

除了上面的六個(gè)大類(lèi),還包含兩個(gè)內(nèi)部封裝的別名校驗(yàn)器,我們已經(jīng)用過(guò) iscolor,還有國(guó)家碼:

錯(cuò)誤處理

Golang 的 error 是個(gè) interface,默認(rèn)其實(shí)只提供了 Error() 這一個(gè)方法,返回一個(gè)字符串,能力比較雞肋。同樣的,validator 返回的錯(cuò)誤信息也是個(gè)字符串:

Key: 'User.Name' Error:Field validation for 'Name' failed on the 'min' tag

這樣當(dāng)然不錯(cuò),但問(wèn)題在于,線(xiàn)上環(huán)境下,很多時(shí)候我們并不是【人工地】來(lái)閱讀錯(cuò)誤信息,這里的 error 最終是要轉(zhuǎn)化成錯(cuò)誤信息展現(xiàn)給用戶(hù),或者打點(diǎn)上報(bào)的。

我們需要有能力解析出來(lái),是哪個(gè)結(jié)構(gòu)體的哪個(gè)屬性有問(wèn)題,哪個(gè) tag 攔截了。怎么辦?

其實(shí) validator 返回的類(lèi)型底層是 validator.ValidationErrors,我們可以在判空之后,用它來(lái)進(jìn)行類(lèi)型斷言,將 error 類(lèi)型轉(zhuǎn)化過(guò)來(lái)再判斷:

err := validate.Struct(mystruct)
validationErrors := err.(validator.ValidationErrors)

底層的結(jié)構(gòu)我們看一下:

// ValidationErrors is an array of FieldError's
// for use in custom error messages post validation.
type ValidationErrors []FieldError
// Error is intended for use in development + debugging and not intended to be a production error message.
// It allows ValidationErrors to subscribe to the Error interface.
// All information to create an error message specific to your application is contained within
// the FieldError found within the ValidationErrors array
func (ve ValidationErrors) Error() string {
	buff := bytes.NewBufferString("")
	var fe *fieldError
	for i := 0; i &lt; len(ve); i++ {
		fe = ve[i].(*fieldError)
		buff.WriteString(fe.Error())
		buff.WriteString("\n")
	}
	return strings.TrimSpace(buff.String())
}

這里可以看到,所謂 ValidationErrors 其實(shí)一組 FieldError,所謂 FieldError 就是每一個(gè)屬性的報(bào)錯(cuò),我們的 ValidationErrors 實(shí)現(xiàn)的 func Error() string 方法,也是將各個(gè) fieldError(對(duì) FieldError 接口的默認(rèn)實(shí)現(xiàn))連接起來(lái),最后 TrimSpace 清掉空格展示。

在我們拿到了 ValidationErrors 后,可以遍歷各個(gè) FieldError,拿到業(yè)務(wù)需要的信息,用來(lái)做日志打印/打點(diǎn)上報(bào)/錯(cuò)誤碼對(duì)照等,這里是個(gè) interface,大家各取所需即可:

// FieldError contains all functions to get error details
type FieldError interface {
	// Tag returns the validation tag that failed. if the
	// validation was an alias, this will return the
	// alias name and not the underlying tag that failed.
	//
	// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
	// will return "iscolor"
	Tag() string
	// ActualTag returns the validation tag that failed, even if an
	// alias the actual tag within the alias will be returned.
	// If an 'or' validation fails the entire or will be returned.
	//
	// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
	// will return "hexcolor|rgb|rgba|hsl|hsla"
	ActualTag() string
	// Namespace returns the namespace for the field error, with the tag
	// name taking precedence over the field's actual name.
	//
	// eg. JSON name "User.fname"
	//
	// See StructNamespace() for a version that returns actual names.
	//
	// NOTE: this field can be blank when validating a single primitive field
	// using validate.Field(...) as there is no way to extract it's name
	Namespace() string
	// StructNamespace returns the namespace for the field error, with the field's
	// actual name.
	//
	// eq. "User.FirstName" see Namespace for comparison
	//
	// NOTE: this field can be blank when validating a single primitive field
	// using validate.Field(...) as there is no way to extract its name
	StructNamespace() string
	// Field returns the fields name with the tag name taking precedence over the
	// field's actual name.
	//
	// eq. JSON name "fname"
	// see StructField for comparison
	Field() string
	// StructField returns the field's actual name from the struct, when able to determine.
	//
	// eq.  "FirstName"
	// see Field for comparison
	StructField() string
	// Value returns the actual field's value in case needed for creating the error
	// message
	Value() interface{}
	// Param returns the param value, in string form for comparison; this will also
	// help with generating an error message
	Param() string
	// Kind returns the Field's reflect Kind
	//
	// eg. time.Time's kind is a struct
	Kind() reflect.Kind
	// Type returns the Field's reflect Type
	//
	// eg. time.Time's type is time.Time
	Type() reflect.Type
	// Translate returns the FieldError's translated error
	// from the provided 'ut.Translator' and registered 'TranslationFunc'
	//
	// NOTE: if no registered translator can be found it returns the same as
	// calling fe.Error()
	Translate(ut ut.Translator) string
	// Error returns the FieldError's message
	Error() string
}

小結(jié)

今天我們了解了 validator 的用法,其實(shí)整體還是非常簡(jiǎn)潔的,我們只需要全局維護(hù)一個(gè) validator 實(shí)例,內(nèi)部會(huì)幫我們做好緩存。此后只需要把結(jié)構(gòu)體傳入,就可以完成校驗(yàn),并提供可以解析的錯(cuò)誤。

validator 的實(shí)現(xiàn)也非常精巧,只不過(guò)內(nèi)容太多,我們今天暫時(shí)覆蓋不到,更多關(guān)于Go 校驗(yàn)庫(kù)validator 的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 在go中使用omitempty的代碼實(shí)例

    在go中使用omitempty的代碼實(shí)例

    今天小編就為大家分享一篇關(guān)于在go中使用omitempty的代碼實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • 詳解golang中Context超時(shí)控制與原理

    詳解golang中Context超時(shí)控制與原理

    Context本身的含義是上下文,我們可以理解為它內(nèi)部攜帶了超時(shí)信息、退出信號(hào),以及其他一些上下文相關(guān)的值,本文給大家詳細(xì)介紹了golang中Context超時(shí)控制與原理,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2024-01-01
  • 揭秘Go Json.Unmarshal精度丟失之謎

    揭秘Go Json.Unmarshal精度丟失之謎

    我們知道在json反序列化時(shí)是沒(méi)有整型和浮點(diǎn)型的區(qū)別,數(shù)字都使用同一種類(lèi)型,在go語(yǔ)言的類(lèi)型中這種共同類(lèi)型就是float64,下面我們就來(lái)探討一下Json.Unmarshal精度丟失之謎吧
    2023-08-08
  • 使用Golang如何實(shí)現(xiàn)簡(jiǎn)易的令牌桶算法

    使用Golang如何實(shí)現(xiàn)簡(jiǎn)易的令牌桶算法

    這篇文章主要介紹了使用Golang如何實(shí)現(xiàn)簡(jiǎn)易的令牌桶算法問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • VS?Code安裝go插件失敗原因分析以及解決方案

    VS?Code安裝go插件失敗原因分析以及解決方案

    vscode安裝go插件時(shí),由于各種原因,在安裝插件時(shí)總是失敗,下面這篇文章主要給大家介紹了關(guān)于VS?Code安裝go插件失敗原因分析以及解決的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • Golang之defer 延遲調(diào)用操作

    Golang之defer 延遲調(diào)用操作

    這篇文章主要介紹了Golang之defer 延遲調(diào)用操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • GO實(shí)現(xiàn)協(xié)程池管理的方法

    GO實(shí)現(xiàn)協(xié)程池管理的方法

    這篇文章給大家介紹GO實(shí)現(xiàn)協(xié)程池管理的方法,分別使用channel實(shí)現(xiàn)協(xié)程池和消費(fèi)者模式實(shí)現(xiàn)協(xié)程池,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-07-07
  • golang beyla采集trace程序原理源碼解析

    golang beyla采集trace程序原理源碼解析

    beyla支持通過(guò)ebpf,無(wú)侵入的、自動(dòng)采集應(yīng)用程序的trace信息,本文以golang的nethttp為例,講述beyla對(duì)trace的采集的實(shí)現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2024-02-02
  • golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法

    golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法

    這篇文章主要給大家介紹了關(guān)于golang中package?is?not?in?GOROOT報(bào)錯(cuò)的真正解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)同樣遇到這個(gè)問(wèn)題的朋友具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • golang?gorm的預(yù)加載及軟刪硬刪的數(shù)據(jù)操作示例

    golang?gorm的預(yù)加載及軟刪硬刪的數(shù)據(jù)操作示例

    這篇文章主要介紹了golang?gorm的預(yù)加載及軟刪硬刪的數(shù)據(jù)操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04

最新評(píng)論

东方市| 小金县| 黄浦区| 甘德县| 互助| 蒙阴县| 韩城市| 茂名市| 韶山市| 咸宁市| 德阳市| 古田县| 固原市| 潞西市| 祁东县| 句容市| 孝感市| 固原市| 井研县| 饶河县| 沁源县| 营山县| 汪清县| 治县。| 普陀区| 重庆市| 绥化市| 疏勒县| 腾冲县| 威信县| 景谷| 庆云县| 白银市| 布拖县| 滨海县| 武隆县| 伊通| 富平县| 尼玛县| 含山县| 庄河市|