2019-09-16 公司項(xiàng)目golang開(kāi)發(fā)指南

一、Mac OS X Go開(kāi)發(fā)環(huán)境搭建

1.安裝 go

https://golang.google.cn/dl/下載對(duì)應(yīng)的go安裝包,然后安裝,如果是macOS x需要 10.10 or later版本

2.環(huán)境配置

環(huán)境變量的配置有系統(tǒng)級(jí)別的和用戶級(jí)別的,/etc/下的profile為系統(tǒng)的環(huán)境變量設(shè)置,對(duì)所有用戶起作用,~/.bash_profile為當(dāng)前用戶的環(huán)境變量設(shè)置,只對(duì)當(dāng)前用戶起作用,一般我們只需要配置~/.bash_profile即可。

(1)工作空間的配置

在寫(xiě)go代碼之前我們需要?jiǎng)?chuàng)建工作空間來(lái)存放go代碼,并把工作空間的目錄保存到環(huán)境變量,一般我們?cè)?HOME目錄下面創(chuàng)建一個(gè)go文件夾,那么工作空間的目錄就是$HOME/go,如果工作空間創(chuàng)建在別的地方就需要設(shè)置GOPATH和GOBIN環(huán)境變量。

(2)環(huán)境變量的配置

$GOROOT?表示 Go 在你的電腦上的安裝位置

$GOBIN?表示編譯器和鏈接器的安裝位置,是運(yùn)行g(shù)o install產(chǎn)生二進(jìn)制文件的目錄

$GOPATH 項(xiàng)目的工作空間的路徑

$GO111MODULE :使用go mod可以方便對(duì)第三方包進(jìn)行管理

根據(jù)約定,GOPATH($HOME/go)下需要?jiǎng)?chuàng)建3個(gè)目錄:

bin 存儲(chǔ)編譯后的可執(zhí)行文件

pkg 存放編譯后生成的包文件

src 存放項(xiàng)目的源碼

終端下面執(zhí)行sudo vim ~/.bash_profile編輯~/.bash_profile ,然后 添加環(huán)境變量

export GOROOT=/usr/local/go

export GOPATH=XXX/XXX/go

export GOBIN=/XXX/XXX/go/bin

export GO111MODULE=on

為了不重啟電腦刷新profile文件需在終端下面執(zhí)行source ~/.bash_profile,然后終端下面執(zhí)行g(shù)o version查看安裝版本,出現(xiàn)go version goX.X.X darwin/amd64,表明安裝成功

可以在終端執(zhí)行g(shù)o env查看go 設(shè)置的環(huán)境變量

3.主要go 命令詳解

go run: 一次性運(yùn)行g(shù)o 源碼程序,把以go結(jié)尾的文件編譯連接形成執(zhí)行文件

go build: 編譯go 源碼

go install: go源碼編譯并打包到 $GOPATH/bin 目錄下, 執(zhí)行g(shù)o install后如果直接執(zhí)行二進(jìn)制文件提示zsh: command not found:XXX,那么在~/.bash_profile添加環(huán)境變量:export PATH=$HOME/go/bin:$PATH,然后執(zhí)行source ~/.bash_profile更新

上面配置完其實(shí)就可以進(jìn)行g(shù)o編程了,但是我們項(xiàng)目的開(kāi)發(fā)還需要繼續(xù)下面的流程。

4.go的刪除

刪除 /usr/local目錄下的 go

刪除 PATH 環(huán)境變量

在/etc/profile 或者 $HOME/.bahs_profile中刪除關(guān)于go環(huán)境變量的設(shè)置

如果是通過(guò) mac os x 的安裝包安裝的,那么應(yīng)該刪除 /etc/paths.d/go 文件

5、goland IDE安裝,必須安裝2018.2及以上版本支持go mod

goland IDE下使用go modules

在goland下,是推薦使用goland配置vgo來(lái)快速使用go modules的。而vgo是基于Go Modules規(guī)范的第三方包管理工具,同官方的go mod命令工具類(lèi)似。對(duì)于通過(guò)goland IDE創(chuàng)建的工程,一定要開(kāi)啟go modules功能,如下圖:

6、依賴(lài)包的安裝go get使用配置

go 1.11 開(kāi)始加入的?go module (vgo),我們可以借助go get命令來(lái)拉取或者更新代碼包及依賴(lài)包,但是由于國(guó)內(nèi)防火墻的原因,很多代碼及依賴(lài)包不能通過(guò)go get獲取,因?yàn)槲覀冃枰鲆恍┡渲脕?lái)解決。go get 命令可以借助代碼管理工具通過(guò)遠(yuǎn)程拉取或更新代碼包及其依賴(lài)包,并自動(dòng)完成編譯和安裝,這個(gè)命令在內(nèi)部實(shí)際上分成了兩步操作:第一步是下載源碼包,第二步是執(zhí)行 go install。為了 go get 命令能正常工作,你必須確保安裝了合適的源碼管理工具,并同時(shí)把這些命令加入你的 PATH 中,再使用go get獲取遠(yuǎn)程包之前,請(qǐng)確保 GOPATH 已經(jīng)設(shè)置。Go 1.8 版本之后,GOPATH 默認(rèn)在用戶目錄的 go 文件夾.

(1)go get通過(guò) git 下載或更新源代碼

我們需要配置一下 git (當(dāng)然,github 或私有倉(cāng)庫(kù)需要配置 ssh key,這里不詳細(xì)介紹)

git config --global url."git@github.com:".insteadOf "https://github.com/"

git config --global url."git@git.querycap.com:".insteadOf "https://git.querycap.com/"

(2)go get通過(guò)golang.org獲取代碼,在這一階段,會(huì)從?https://go.googlesource.com獲取代碼,我們需要通過(guò)github上面的鏡像獲取,配置git如下:

git config --global url."git@github.com:golang/".insteadOf "https://go.googlesource.com/"

配置完成可以通過(guò)git config -l查看

url.git@github.com:golang/.insteadof=https://go.googlesource.com/

url.git@git.querycap.com:.insteadof=https://git.querycap.com/

url.git@github.com:.insteadof=https://github.com/

7、依賴(lài)包管理工具go mod的使用

(1). go mod使用配置

從go 1.11開(kāi)始,go支持新的依賴(lài)包管理工具go mod,由于一些第三方包國(guó)內(nèi)不能下載,所以需要設(shè)置GOPROXY(默認(rèn)國(guó)內(nèi)不能訪問(wèn)的https://proxy.golang.org)和GONOSUMDB,在終端執(zhí)行vim ~/.bash_profile,新加環(huán)境變量GOPROXY和GONOSUMDB

export GOPROXY=https://goproxy.cn,direct ?//通過(guò)七牛云代理來(lái)下載第三方庫(kù)包

export GONOSUMDB=git.querycap.com/* ? ? //不需要哈希檢查git.querycap.com/下的庫(kù)包

如果GOSUMDB不為空,最好把它設(shè)置為空

(2). go mod原理

在項(xiàng)目的根目錄下面執(zhí)行g(shù)o mod init,創(chuàng)建一個(gè)go.mod文件,當(dāng)執(zhí)行g(shù)o文件的時(shí)候,go mod 會(huì)自動(dòng)查找依賴(lài)自動(dòng)下載

go mod是Go項(xiàng)目的依賴(lài)描述文件該文件主要用來(lái)描述兩個(gè)事情:

《1》當(dāng)前項(xiàng)目名(module)是什么。每個(gè)項(xiàng)目都應(yīng)該設(shè)置一個(gè)名稱(chēng),當(dāng)前項(xiàng)目中的包(package)可以使用該名稱(chēng)進(jìn)行相互調(diào)用,比如我的項(xiàng)目目錄是在$HOME/go/src/git.querycap.com/practice/srv-demo-yzhl,那么module就是git.querycap.com/practice/srv-demo-yzhl,項(xiàng)目中的包引用的時(shí)候就可以import "git.querycap.com/practice/srv-demo-yzhl/XXXX"

《2》當(dāng)前項(xiàng)目依賴(lài)的第三方包名稱(chēng)。項(xiàng)目運(yùn)行時(shí)會(huì)自動(dòng)分析項(xiàng)目中的代碼依賴(lài),生成go.sum依賴(lài)分析結(jié)果,隨后go編譯器會(huì)去下載這些第三方包,然后再編譯運(yùn)行。

go.sum依賴(lài)分析文件,記錄每個(gè)依賴(lài)庫(kù)的版本和哈希值

一般情況下,go.sum應(yīng)當(dāng)被添加到版本管理中隨著go.mod文件一起提交。

? ?(3). go mod常用命令:

? ?go mod init moduleName // 初始化modules

? ? go mod download: //下載依賴(lài)的module到本地cache

? ? go mod edit //編輯go.mod文件,選項(xiàng)有-json、-require和-exclude,可以使用幫助go help mod edit

? ? go mod graph // 以文本模式打印模塊需求圖

? ? go mod tidy //檢查,刪除錯(cuò)誤或者不使用的modules,以及添加缺失的模塊

? ? go mod verify // 驗(yàn)證依賴(lài)是否正確

? ? go mod why //解釋為什么需要依賴(lài)

(4). go mod 升級(jí)依賴(lài)包

?go get -u?將會(huì)升級(jí)到最新的次要版本或者修訂版本 (x.y.z, z 是修訂版本號(hào) y 是次要版本號(hào))?

?go get -u=patch?將會(huì)升級(jí)到最新的修訂版本?

?go get package@version?將會(huì)升級(jí)到指定的版本

二、公司的項(xiàng)目目錄及代碼目錄結(jié)構(gòu)

在設(shè)置的GOPATCH里面創(chuàng)建src目錄,然后在里面創(chuàng)建git.querycap.com目錄,再創(chuàng)建組目錄(比如我demo項(xiàng)目的組是practice),再創(chuàng)建項(xiàng)目目錄(比如我的demo項(xiàng)目是srv-demo-yzhl)。后面我們通過(guò)項(xiàng)目實(shí)踐來(lái)一步一步介紹代碼的目錄結(jié)構(gòu)

三、安裝常用工具包tools

安裝tools(必須在go mod之后),使用go get -u git.querycap.com/tools/cmd/tools獲取安裝(如果獲取不成功就到https://git.querycap.com/tools/cmd目錄把tools pull下來(lái)放到工作空間的src/git.querycap.com/tools里面), 執(zhí)行make命令安裝tools,安裝完成之后在終端執(zhí)行tools命令看看是否成功

或者執(zhí)行g(shù)o install git.querycap.com/tools/cmd/cmd/tools@v1.12.4安裝,安裝到GOBIN目錄下面

四、項(xiàng)目實(shí)踐

下面我們通過(guò)一步一步構(gòu)建工程來(lái)說(shuō)明項(xiàng)目代碼的目錄結(jié)構(gòu)及公司go項(xiàng)目中數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)庫(kù)、緩存及中間件auth的使用

1.最小項(xiàng)目代碼結(jié)構(gòu):

在項(xiàng)目目錄里面(比如我的demo項(xiàng)目的srv-messageDemo-yzhl里面)執(zhí)行g(shù)o mod init和建立main.go文件,添加main函數(shù),然后在mian.go目錄下面在終端執(zhí)行g(shù)o run main.go,最小的工程已經(jīng)生成,生成的代碼結(jié)構(gòu)如圖:

(1)config下面的default.yml配置文件保存了項(xiàng)目用到的配置,比如數(shù)據(jù)庫(kù)的host,密碼,用戶;redis的host及密碼,為了使用本地的數(shù)據(jù)庫(kù),需要建立local.yml(local.yml不上傳到git代碼倉(cāng)庫(kù)里面,只供本地調(diào)試使用),依次類(lèi)推需要建立各個(gè)環(huán)境的配置文件,比如stage.yml(測(cè)試環(huán)境),demo.yml(演示環(huán)境),master.yml(線上環(huán)境),當(dāng)前我的項(xiàng)目default.yml配置如下:

GOENV: DEV

(2)helmx.default.yml:配置項(xiàng)目基本信息,這些信息將在部署時(shí)作為環(huán)境變量配置,我的項(xiàng)目最終helmx.default.yml配置如下:

service: {}

(3)go.sum依賴(lài)分析文件,記錄每個(gè)依賴(lài)庫(kù)的版本和哈希值

git.querycap.com/tools/servicex v1.3.2 ?h1:lctzJQV4vg8rF7vgcC4xh4E5t9D0HYo7ZVIOzb50tkw=

git.querycap.com/tools/servicex v1.3.2/go.mod ? h1:fW/KvNvHPrC6GUyNXlU1GQAu06LrdlcS92hNvCSyj7c=

github.com/BurntSushi/toml v0.3.1/go.mod ?h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod ? h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=

2.實(shí)現(xiàn)查看openapi.json文件內(nèi)容的api

我們先來(lái)看通過(guò)postman查看openapi.json內(nèi)容的請(qǐng)求如下:

要實(shí)現(xiàn)上述功能我們需要用到第三方的Courier微服務(wù)的庫(kù),Courier最小的單位是Operator,任何的struct都是一個(gè)Operator,Operator有一個(gè)Output(ctx context.Context) (interface{}, error)方法,struct結(jié)構(gòu)定義Operator的輸入?yún)?shù),Output(ctx context.Context) (interface{}, error)根據(jù)輸入?yún)?shù)返回成功或者失敗的結(jié)果,Router是Operator的載體,每個(gè)Router至少包含一個(gè)Operator。下面是路由的例子說(shuō)明:

先定義三個(gè)路由RouterRoot,RouterA,RouterB

var RouterRoot = courier.NewRouter(&OperatorRoot{})

var RouterA = courier.NewRouter(&OperatorA{})

var RouterB = courier.NewRouter(&OperatorB{})

在初始方法里面注冊(cè)路由,把路由連接起來(lái)

func init() {

? ? RouterRoot.Register(RouterA)

? ? RouterRoot.Register(RouterB)

? ? RouterA.Register(courier.NewRouter(&OperatorA1{}, &OperatorA2{}))

? ? RouterA.Register(courier.NewRouter(&OperatorA3{}, &OperatorA4{}))

? ? RouterB.Register(courier.NewRouter(&OperatorB1{}, &OperatorB2{}))

}

最終得到的路由如下:

? ? OperatorRoot -> OperatorA -> OperatorA1 -> OperatorA2

? ? ?OperatorRoot -> OperatorA -> OperatorA3 -> OperatorA4

? ? OperatorRoot -> OperatorB -> OperatorB1 -> OperatorB2

那下面我們需要在工程里面先創(chuàng)建routers文件夾,所有后續(xù)操作的路由都在此文件夾里面創(chuàng)建,然后再建立路由root.go文件,內(nèi)容如下:

由于我們實(shí)現(xiàn)的是RESTful API,所以我們操作使用的是Courier里面RESTful API的承載者h(yuǎn)ttp transport 里面的操作httptransport.Group("/")和httptransport.Group("/demo-yzhl")

routers完成后我們?cè)賱?chuàng)建global文件夾,里面創(chuàng)建config.go文件,在里面定義項(xiàng)目全局使用的變量及項(xiàng)目需要使用的service,目前我們只需定義Server全局變量,內(nèi)容如下:

然后我們?cè)趍ain函數(shù)里面執(zhí)行courier.Run(routers.RootRouter,global.Server),內(nèi)容如下:

最后在項(xiàng)目根目錄下面執(zhí)行命令tools openapi,項(xiàng)目工程顯示新生成的openapi.json文件,注意openapi.json不需要傳到git倉(cāng)庫(kù)

在項(xiàng)目目錄里面在終端執(zhí)行g(shù)o run main.go,輸出如下信息:

然后在瀏覽器或者postman里面輸入localhost/demo-yzhl/既可以看到上面openapi.json內(nèi)容,終止程序運(yùn)行同時(shí)執(zhí)行ctrl+c

3.實(shí)現(xiàn)包含登錄、注冊(cè)、查詢及用戶校驗(yàn)的API

(1)由于項(xiàng)目中需要用到一些枚舉常量,先建立constants文件夾來(lái)存放項(xiàng)目中使用的枚舉常量,然后再建errors文件夾存放狀態(tài)錯(cuò)誤碼的枚舉常量,狀態(tài)碼為9位,組成如下:

status code serivce code auto-increased id

500 ? ? ? ? ? ? ? 001 ? ? ? ? ? ? ? ? ? ?001

再建立status_error.go文件,內(nèi)容如下:

package errors

import "net/http"

//go:generate tools gen error StatusError

type StatusError int

func (StatusError) ServiceCode() int? {

return 999 *1e3

}

//400 錯(cuò)誤的請(qǐng)求,服務(wù)器不理解請(qǐng)求的語(yǔ)法

const (

StatusBadRequestError StatusError =http.StatusBadRequest*1e6 + iota + 1

)

//401 未授權(quán),請(qǐng)求需要身份驗(yàn)證

const (

StatusUnauthorized StatusError =http.StatusUnauthorized*1e6 + iota + 1

)

//403 禁止,服務(wù)器拒絕請(qǐng)求

const (

ForbiddenError StatusError =http.StatusForbidden*1e6 +iota +1

)

//404 未找到

const (

NotFoundError StatusError =http.StatusNotFound*1e6 +iota +1

? // @errTalk 用戶不存在

? UserNotFound

? // @errTalk account id 不存在

)

//409 沖突

const (

ConflictError StatusError =http.StatusConflict*1e6 ?+ iota ?+ 1

? // @errTalk 用戶沖突

? UserConflictError

)

// 500 服務(wù)器內(nèi)部錯(cuò)誤

const (

InternalServerError StatusError =http.StatusInternalServerError*1e6 ?+ iota + 1

)

進(jìn)入errors目錄在終端執(zhí)行tools gen error StatusError或者點(diǎn)擊//go:generate tools gen error StatusError這行代碼左邊的綠色箭頭選擇第三項(xiàng)產(chǎn)生可以使用的StatusError代碼文件

(2)由于用戶需要有唯一標(biāo)志user id,所以使用client的服務(wù)來(lái)產(chǎn)生user id

建立clients文件夾并創(chuàng)建gen.go文件,gen.go文件內(nèi)容為:

//go:generate tools gen client id --spec-url http://srv-id.base.d.rktl.work/id

生成代碼的方式如上面生成StatusError相同,生成完后代碼目錄結(jié)構(gòu)如下:

引入了client服務(wù)之后,我們需要使用client服務(wù)產(chǎn)生user id,下面我們建立modules文件夾來(lái)存放項(xiàng)目中的各功能模塊。功能模塊的go文件文件命名為功能模塊名字_mgr.go,例如產(chǎn)生user id的go文件名為id_mgr.go,內(nèi)容如下:

package id

import (

"context"

"git.querycap.com/practice/srv-demo-yzhl/clients/client_id"

"git.querycap.com/practice/srv-demo-yzhl/constants/errors"

"git.querycap.com/tools/datatypes"

"github.com/go-courier/metax"

)

//定義生成user id的結(jié)構(gòu)體IDMgr

type IDMgr struct {

c client_id.ClientID

? metax.Ctx

}

//根據(jù)clentid服務(wù)創(chuàng)建一個(gè)IDMgr對(duì)象

func NewIDMgr(c client_id.ClientID) *IDMgr {

return &IDMgr{

c:c,

}

}

//根據(jù)上一個(gè)操作的上下文來(lái)創(chuàng)建一個(gè)IDMgr對(duì)象

func (idmgr *IDMgr) WithContent(ctx context.Context) ?*IDMgr? {

return &IDMgr{

c:idmgr.c.WithContext(ctx),

Ctx:idmgr.Ctx.WithContext(ctx),

}

}

//生成user id

func (idmgr *IDMgr) GenerateID() ?(datatypes.UUID, error)? {

resp, ?_, err :=idmgr.c.GenerateID()

if err !=nil {

return 0,errors.InternalServerError

? }

return datatypes.UUID(resp.ID),nil

}

然后在global文件夾下面的config.go里面創(chuàng)建一個(gè)全局變量IDMgr在項(xiàng)目中使用,代碼結(jié)構(gòu)及config.go代碼如下:

上面完成之后我們還需要在config.go里面初始化client服務(wù)器的配置及在local.yml里面配置client服務(wù)器的host才能使用client服務(wù),config.go的初始化配置如下:

初始化方法init里面第一步設(shè)置服務(wù)器的名字,建議和項(xiàng)目名字一致,第二步設(shè)置我們需要的config,當(dāng)前我們用到了Log,Server,Client配置,后面用到的redis,數(shù)據(jù)庫(kù)的配置都需要在Config結(jié)構(gòu)圖里面添加,第三步對(duì)使用到的配置進(jìn)行可用性檢查。在項(xiàng)目目錄下面執(zhí)行g(shù)o run main.go,發(fā)現(xiàn)default多了一些配置:

SRV_DEMO_YZHL__ClientID_Host:""

SRV_DEMO_YZHL__ClientID_Protocol:""

SRV_DEMO_YZHL__Log_Level: DEBUG

我們?cè)赾onfig文件夾下面建立local.yml(程序運(yùn)行的時(shí)候首先從local.yml里面讀取配置,local.yml只供本地使用,不上傳到git倉(cāng)庫(kù)),然后把上面內(nèi)容復(fù)制到local.yml,并設(shè)置SRV_DEMO_YZHL__ClientID_Host,local.yml代碼如下:

GOENV: DEV

SRV_DEMO_YZHL__ClientID_Host: srv-id.base.d.rktl.work

SRV_DEMO_YZHL__ClientID_Protocol:""

SRV_DEMO_YZHL__Log_Level: DEBUG

(3)我們來(lái)實(shí)現(xiàn)用戶的注冊(cè)、登錄、校驗(yàn)、查找等功能

《1》首先我們?cè)赾onstans文件夾里面創(chuàng)建types文件夾存放項(xiàng)目用到的其它常量,然后新建user_state.go文件里面定義用戶狀態(tài)枚舉常量,并生成項(xiàng)目使用的用戶狀態(tài)文件user_state__generated.go,生成方法如erros里面方法一樣,代碼如下:

注意:user_state.go里面的枚舉常量第一個(gè)UNKNOWN前面是一個(gè)下劃線,后面ENABLED和DISANLED前面都是兩個(gè),如果后面下劃線是一個(gè)的話,//后面的信息(啟用用戶、禁用用戶)就不能返回

《2》數(shù)據(jù)庫(kù)的使用:用戶信息的保存需要用到數(shù)據(jù)庫(kù),項(xiàng)目當(dāng)中使用的數(shù)據(jù)庫(kù)是Postgres,在使用數(shù)據(jù)庫(kù)之前先在本地電腦上面安裝三個(gè)軟件:Postgres是數(shù)據(jù)庫(kù)軟件,Postman是調(diào)試api的軟件,Navicat Premium是對(duì)數(shù)據(jù)庫(kù)里面的表進(jìn)行管理的軟件

首先在項(xiàng)目里面新建database文件夾,然后新建databse的db.go及用戶表結(jié)構(gòu)的user.go,代碼如下:

db.go

user.go

解釋user.go:

// @def unique_index I_user_id UserID

表示為UserID建立唯一索引,索引名字為I_user_id

//go:generate tools gen model2 User -t t_yzhl_user --database DBDemo

User:產(chǎn)生用戶表使用的struct

model2 :generate interfaces of db mode

t_yzhl_user:數(shù)據(jù)庫(kù)paractice_demo_message中用戶表的名字

DBDemo:paractice_demo_message

最終產(chǎn)生的go文件名字為user__generated.go,產(chǎn)生方式同上面講的errors里面產(chǎn)生錯(cuò)誤碼的方式一樣,user__generated.go里面包括了對(duì)用戶表的操作,供我們?cè)陧?xiàng)目中調(diào)用。

為了使用數(shù)據(jù)庫(kù),我們需要在global文件夾里面的config.go添加數(shù)據(jù)庫(kù)Postgres的配置,添加后的代碼如下:

執(zhí)行g(shù)o run main.go, default.yml會(huì)出現(xiàn)Postgres的本地配置,

SRV_DEMO_YZHL__Postgres_Host: 127.0.0.1

SRV_DEMO_YZHL__Postgres_Password:""

SRV_DEMO_YZHL__Postgres_SlaveHost:""

SRV_DEMO_YZHL__Postgres_User:""

然后復(fù)制到local.yml里面

打開(kāi)上面安裝的Postgress和Navicat Premium,在Postgress新建一個(gè)server:

在Navicat Premium里面連接剛才建立的pracice_demo_message的server,然后在main初始化方法里面添加要執(zhí)行的命令,其中migrage就是生成數(shù)據(jù)庫(kù)及表的命令。

添加完后執(zhí)行g(shù)o run main.go?migrage生成名為pracice_demo_message的db及t_yzhl_user的表,在Navicat Premium里面顯示如下:

《3》在module文件夾下面新建user文件夾,里面存放user的功能模塊

用戶的注冊(cè)功能:

先在user文件夾里面新建user_param.go,里面定義CreateUserBody結(jié)構(gòu)體包含注冊(cè)需要的用戶名和密碼

type CreateUserBody struct {

//昵稱(chēng) 大于等于三個(gè)字符,在json結(jié)構(gòu)里面的key為nickname

? Nickname string `json:"nickname" validate:"@char[3,]"`

? //密碼 大于等于6個(gè)字符,在json結(jié)構(gòu)里面的key為password

? Password string `json:"password" validate:"@char[6,]"`

}

然后在user文件夾里面新建user_mgr.go,定義三個(gè)生成UserMgr的方法:

在global里面的config.go定義UserMgr全局變量供項(xiàng)目使用

var (

IDMgr =id.NewIDMgr(ClientID)

UserMgr =user.NewUserMgr(IDMgr,Postgres)

)

由于有注冊(cè),登錄,查詢等功能,我們?cè)傩陆ㄒ粋€(gè)user_mgr_user.go文件,里面定義注冊(cè)、登錄、查詢等用戶的方法,先定義注冊(cè)用戶的方法CreateUser,代碼如下:

《4》實(shí)現(xiàn)注冊(cè)的RESTful API:

先在routes文件夾下面新建user文件夾,然后新建root.go及create_user.go

root.go里面定義UserRouter變量:var UserRouter =courier.NewRouter(httptransport.Group("/users"))

在routers下面的root.go的init方法里面添加路由:

RootRouter.Register(user.UserRouter)

對(duì)于RESTful API,需要http method?and?pattern path去分發(fā)路由

create_user.go代碼如下:

在項(xiàng)目目錄下面執(zhí)行g(shù)o run main.go,運(yùn)行成功后:

打開(kāi)Postman,新建一個(gè)post方法,body里面的nickname和password以json結(jié)構(gòu)傳輸,URI為localhost/v0/users,點(diǎn)擊Send,收到下面的response,到目前為止注冊(cè)用戶的RESTFul API就完成了。

刷新Navicat Premium,用戶表顯示注冊(cè)的nickname1用戶:

《5》實(shí)現(xiàn)登錄的RESTFul API

步驟和上面注冊(cè)的API相同,先在user_mgr_usr.go添加根據(jù)昵稱(chēng)和密碼登錄的方法FetchUserWithNicknameAndPwd:

為了便于閱讀和維護(hù),一個(gè)API一個(gè)文件,所以在routers的user文件夾里面新建login_user.go,代碼如下:

其中LoginUserRouter路由在user文件夾里面的root.go定義

package user

import (

"github.com/go-courier/courier"

"github.com/go-courier/httptransport"

)

func init()? {

UserRouter.Register(LoginUserRouter)

}

var UserRouter =courier.NewRouter(httptransport.Group("/users"))

var LoginUserRouter =courier.NewRouter(httptransport.Group("/login"))

登錄成功之后生成token我們實(shí)用到了第三方庫(kù)jwt,jwt的使用大家可以自行百度,所以同生成user id功能類(lèi)似,我們需要在module文件夾下面新建token文件夾,里面新建token_mgr.go來(lái)生成token,代碼如下:

然后在global的config.go里面定義TokenMgr全局變量供項(xiàng)目使用:

var (

IDMgr =id.NewIDMgr(ClientID)

UserMgr =user.NewUserMgr(IDMgr,Postgres)

TokenMgr =token.NewTokenMgr(JwtSecretKey)

)

在項(xiàng)目目錄下面執(zhí)行g(shù)o run main.go命令,終端顯示信息如下:

根據(jù)終端的輸出信息顯示注冊(cè)和登錄都是post方法,數(shù)據(jù)都是放body里面,如果上面不重新定義LoginUserRouter,注冊(cè)和登錄的URI都是localhost/v0/users,這是不允許的,所以我們重新定義了LoginUserRouter路由

在Postman里面測(cè)試登錄的API:

《6》實(shí)現(xiàn)根據(jù)用戶的userID查找用戶的API:

查找用戶的時(shí)候需要對(duì)header里面的token進(jìn)行驗(yàn)證,所以需要用到中間件authorization。在routers下面新建middleware文件夾,里面新建auth_user.go,代碼如下:

package middleware

import (

"context"

"git.querycap.com/practice/srv-demo-yzhl/constants/errors"

"git.querycap.com/practice/srv-demo-yzhl/databases"

"git.querycap.com/practice/srv-demo-yzhl/global"

"git.querycap.com/tools/authorization"

"github.com/dgrijalva/jwt-go"

"reflect"

"strconv"

)

type ContextAccountAuth struct {

//Bearer access_token

? Authorization string `name:"Authorization,omitempty" in:"header"`

}

var contextAccountAuthKey =reflect.TypeOf(ContextAccountAuth{}).String()

func (req ContextAccountAuth)ContextKey()string? {

return? contextAccountAuthKey

}

func GetUserFromContext(c context.Context) *databases.User? {

return c.Value(contextAccountAuthKey).(*databases.User)

}

func (req ContextAccountAuth)Output(ctx context.Context) (interface{},error)? {

var userID databases.UUID

? accountStr :=authorization.ParseAuthorization(req.Authorization).Get("Bearer")

if accountStr !="" {

tokenMgr :=global.TokenMgr.WithContent(ctx)

claims,err :=tokenMgr.ParseToken(accountStr)

if err !=nil {

return nil,err

? ? ? }

tempUserID,_ :=strconv.ParseUint(claims.(jwt.MapClaims)["userID"].(string),10,64)

userID =databases.UUID(tempUserID)

if userID ==0 {

return nil,errors.AccountIDNotFoundError

? ? ? }

user,err :=global.UserMgr.WithContext(ctx).FetchUserByUserID(userID)

if err !=nil {

return nil,err

? ? ? }

return user,nil

? }

return nil,errors.TokenInValidError

}

其中tokenMgr.ParseToken,在token_mgr.go,代碼如下:

FetchUserByUserID在user_mgr_user.go里面:

中間件的功能完成之后,下面的根據(jù)用戶的userID查找用戶的實(shí)現(xiàn)方式和注冊(cè)、登錄用戶流程一樣:

上面已經(jīng)在user_mgr_user.go里面實(shí)現(xiàn)了根據(jù)用戶的userID查找用戶的功能FetchUserByUserID,下面我們?cè)趓outers下面的user里面新建實(shí)現(xiàn)根據(jù)userID查找用戶的api的文件get_user.go

其中AuthUserRouter在user下面的root.go定義,截止目前root.go代碼如下:

終端執(zhí)行g(shù)o run main.go:

在Postman測(cè)試根據(jù)用戶的id查找用戶:

到此為止,我們實(shí)現(xiàn)的用戶注冊(cè)、登錄、查找功能就實(shí)現(xiàn)完畢,當(dāng)中用到了數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)庫(kù)、中間件authorization使用

五、CI初始化

在項(xiàng)目目錄下面執(zhí)行:go get -u git.querycap.com/infra/hx,然后執(zhí)行

hx init,項(xiàng)目目錄下面多了helms.project.yml

創(chuàng)建.gitlab-ci.yml為持續(xù)集成使用

六、創(chuàng)建makefile編譯使用:touch makefile

七、創(chuàng)建.gitignore文件:touch .gitignore, 在里面添加需要忽略的文件

openapi.json,config/local.yml

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容