時(shí)間:2023-06-02 01:39:01 | 來源:網(wǎng)站運(yùn)營
時(shí)間:2023-06-02 01:39:01 來源:網(wǎng)站運(yùn)營
從0開始Go語言-用Golang搭建網(wǎng)站(無依賴):Gopher:原譯是囊地鼠,也就是Go語言Logo的那個(gè)小可愛;這里特指Go程序員給自己的昵稱。
GOPATH環(huán)境變量指定工作區(qū)的位置。如果沒有設(shè)置GOPATH,則假定在Unix系統(tǒng)上為GOPATH環(huán)境變量是用于設(shè)置Go編譯可以執(zhí)行文件、包源碼以及依賴包所必要的工作目錄路徑,Go1.11后,新的木塊管理雖然可以不再依賴$HOME/go
,在Windows上為%USERPROFILE%/go
。如果要將自定義位置用作工作空間,可以設(shè)置GOPATH環(huán)境變量。
$GOPATH/src
,但是依然需要使用 $GOPATH/pkg
路徑來保存依賴包。GOPATH
:$ export GOPATH=$YOUR_PATH/go
$ source ~/.bash_profile
控制面板->系統(tǒng)->高級(jí)系統(tǒng)設(shè)置->高級(jí)->環(huán)境變量設(shè)置
go install
編譯的可執(zhí)行二進(jìn)制文件go install
編譯后的包文件,就會(huì)存放在這里go get
命令下載的源碼包文件$ go env
GOARCH="amd64"GOBIN=""GOCACHE="/Users/zeta/Library/Caches/go-build"GOEXE=""GOFLAGS=""GOHOSTARCH="amd64"GOHOSTOS="darwin"GOOS="darwin"GOPATH="/Users/zeta/workspace/go"GOPROXY="https://goproxy.io"GORACE=""GOROOT="/usr/local/go"GOTMPDIR=""GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"GCCGO="gccgo"CC="clang"CXX="clang++"CGO_ENABLED="1"GOMOD=""CGO_CFLAGS="-g -O2"CGO_CPPFLAGS=""CGO_CXXFLAGS="-g -O2"CGO_FFLAGS="-g -O2"CGO_LDFLAGS="-g -O2"PKG_CONFIG="pkg-config"GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/7v/omg2000000000000019/T/go-build760324613=/tmp/go-build -gno-record-gcc-switches -fno-common"
$ mkdir gowebserver && cd gowebserver
$ touch main.go
package mainimport "fmt"func main() { fmt.Println("Hello, 世界")}
$ go run main.go
Hello, 世界
package
申明包 & import
導(dǎo)入包package
關(guān)鍵字。package
關(guān)鍵字必須是第一行出現(xiàn)的代碼。main
import
關(guān)鍵字。默認(rèn)情況下,導(dǎo)入包的包名與導(dǎo)入路徑的最后一個(gè)元素一致,例如 import "math/rand"
,在代碼中使用這個(gè)包時(shí),直接使用rand
,例如 rand.New()
import "fmt"import "math/rand"
或者 分組import ( "fmt" "math/rand")
fmt包是Go語言內(nèi)建的包,作用是輸出打印。
func
關(guān)鍵字:定義函數(shù)func
是function的縮寫, 在Go語言中是定義函數(shù)的關(guān)鍵字。func 函數(shù)名(參數(shù)1 類型,參數(shù)2 類型){ 函數(shù)體}
本例中定義了一個(gè)main函數(shù)。main
函數(shù)沒有參數(shù)。main
函數(shù)體里調(diào)用fmt
包的Println
函數(shù),在控制臺(tái)輸出字符串 “Hello, 世界”main.main()
,所以每一個(gè)可執(zhí)行的Go程序都應(yīng)該有一個(gè)main
包和一個(gè)main函數(shù)
。main.go
文件,修改代碼如下:package mainimport ( "fmt" "net/http")func myWeb(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "這是一個(gè)開始")}func main() { http.HandleFunc("/", myWeb) fmt.Println("服務(wù)器即將開啟,訪問地址 http://localhost:8080") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("服務(wù)器開啟錯(cuò)誤: ", err) }}
保存文件,然后在命令行工具下輸入命令,運(yùn)行程序$ go run main.go
fmt.Println
打印出來的提示,在瀏覽器中訪問 http://localhost:8080
你將訪問到一個(gè)頁面,顯示 "這是一個(gè)開始"package main
,然后導(dǎo)入包。net/http
,這個(gè)包是官方的,實(shí)現(xiàn)http客戶端和服務(wù)端的各種功能。Go語言開發(fā)Web服務(wù)的所有功能就是基于這個(gè)包(其他第三方的Go語言Web框架也都基于這個(gè)包,沒有例外)main
函數(shù)里發(fā)生了什么http.HandleFunc("/", myWeb)
myWeb
。err := http.ListenAndServe(":8080", nil)
在這句,調(diào)用了http
包中的ListenAndServe
函數(shù),該函數(shù)有兩個(gè)參數(shù),第一個(gè)是指定監(jiān)聽的端口號(hào),第二個(gè)是指定處理請(qǐng)求的handler,通常這個(gè)參數(shù)填nil,表示使用默認(rèn)的ServeMux作為handler。nil
就是其他語言里的null
。什么是handler?什么是ServeMux?
ServeMux就是一個(gè)HTTP請(qǐng)求多路由復(fù)用器。它將每個(gè)傳入請(qǐng)求的URL與已注冊(cè)模式的列表進(jìn)行匹配,并調(diào)用與URL最匹配的模式的處理程序。
很熟悉吧?還記得前面的http.HandleFunc
嗎?他就是給http包中默認(rèn)的ServeMux(DefaultServeMux)添加URL與處理函數(shù)匹配。
通常都是使用http包中的默認(rèn)ServeMux,所以在http.ListenAndServe
函數(shù)的第二個(gè)參數(shù)提供nil就可以了
ListenAndServe
函數(shù)會(huì)一直監(jiān)聽,除非強(qiáng)制退出或者出現(xiàn)錯(cuò)誤。err
變量接收返回對(duì)象。緊接著,判斷err
是否為空,打印出錯(cuò)誤內(nèi)容,程序結(jié)束。var
var str string = "my string"//^ ^ ^//關(guān)鍵字 變量名 類型
Go還提了一種簡單的變量定義方式:=
,自動(dòng)根據(jù)賦值的對(duì)象定義變量類型,用起來很像腳本語言:str := "my string"
if err != nil{ //處理....}
在Go語言中,這是很常見的錯(cuò)誤處理操作,另一種panic異常,官方建議不要使用或盡量少用,暫不做介紹,先從err開始。if err := http.ListenAndServe(":8080", nil); err != nil { fmt.Println("服務(wù)器開啟錯(cuò)誤: ", err)}
err這個(gè)變量的生命周期只在if
塊中有效。main
函數(shù)中,用http.HandleFunc
將 myWeb與路由/
匹配在一起。HandleFunc
函數(shù)定義了兩個(gè)參數(shù)w
,r
,參數(shù)類型分別是http.ResponseWriter
和*http.Request
,w
是響應(yīng)留寫入器,r
是請(qǐng)求對(duì)象的指針。*
標(biāo)記類型,說明這個(gè)參數(shù)需要的是這個(gè)類型的對(duì)象的指針。/
,請(qǐng)求對(duì)象和響應(yīng)流寫入器被傳遞給myWeb
函數(shù),并由myWeb
函數(shù)負(fù)責(zé)處理這次請(qǐng)求。&
,取值用*
,例如:mystring := "hi"//取指針mypointer := &mystring//取值mystring2 := *mypointerfmt.Println(mystring,mypointer,mystring2)
把這些代碼放在main
函數(shù)里,$ go run main.go
運(yùn)行看看fmt.Fprintf(w, "這是一個(gè)開始")
再一次遇到老熟人fmt
,這次使用他的Fprintf
函數(shù)將字符串“這是一個(gè)開始”,寫入到w
響應(yīng)流寫入器對(duì)象。w
響應(yīng)流寫入器里寫入的內(nèi)容最后會(huì)被Response輸出到用戶瀏覽器的頁面上。/
路由main.go
文件,修改myWeb
函數(shù),如下:func myWeb(w http.ResponseWriter, r *http.Request) { r.ParseForm() //它還將請(qǐng)求主體解析為表單,獲得POST Form表單數(shù)據(jù),必須先調(diào)用這個(gè)函數(shù) for k, v := range r.URL.Query() { fmt.Println("key:", k, ", value:", v[0]) } for k, v := range r.PostForm { fmt.Println("key:", k, ", value:", v[0]) } fmt.Fprintln(w, "這是一個(gè)開始")}
運(yùn)行程序$ go run main.go
curl --request POST / --url 'http://localhost:8080/?name=zeta' / --header 'cache-control: no-cache' / --header 'content-type: application/x-www-form-urlencoded' / --data description=hello
頁面和終端命令行工具會(huì)答應(yīng)出以下內(nèi)容:key: name , value: zetakey: description , value: hello
http
請(qǐng)求的所有內(nèi)容,都保存在http.Request
對(duì)象中,也就是myWeb
獲得的參數(shù) r
。r.ParseForm()
,作用是填充數(shù)據(jù)到 r.Form
和 r.PostForm
r.URL.Query()
函數(shù)返回的值 和 r.PostForm
值里的每一個(gè)參數(shù)。r.URL.Query()
和 r.PostForm
分別是URL參數(shù)對(duì)象和表單參數(shù)對(duì)象string
,值的類型是string
數(shù)組。在http協(xié)議中,無論URL和表單,相同名稱的參數(shù)會(huì)組成數(shù)組。循環(huán)遍歷:for...range
for
關(guān)鍵字,以下是Go中4種for
循環(huán)//無限循環(huán),阻塞線程,用不停息,慎用!for{}//條件循環(huán),如果a<b,循環(huán),否則,退出循環(huán)for a < b{}//表達(dá)式循環(huán),設(shè)i為0,i小于10時(shí)循環(huán),每輪循環(huán)后i增加1for i:=0; i<10; i++{}//for...range 遍歷objs,objs必須是map、slice、chan類型for k, v := range objs{}
前3種,循環(huán)你可以看作條件循環(huán)的變體(無限循環(huán)就是無條件的循環(huán))。for...range
循環(huán),遍歷可遍歷對(duì)象,并且每輪循環(huán)都會(huì)將鍵和值分別賦值給變量 k
和 v
html/template
"main
函數(shù)不變,增加導(dǎo)入html/template
包,然后修改myWeb
函數(shù),如下:import ( "fmt" "net/http" "text/template" //導(dǎo)入模版包)func myWeb(w http.ResponseWriter, r *http.Request) { t := template.New("index") t.Parse("<div id='templateTextDiv'>Hi,{{.name}},{{.someStr}}</div>") data := map[string]string{ "name": "zeta", "someStr": "這是一個(gè)開始", } t.Execute(w, data) // fmt.Fprintln(w, "這是一個(gè)開始")}
在命令行中運(yùn)行 $ go run main.go
,訪問 http://localhost:8080
<div id='templateTextDiv'>Hi,{{.name}},{{.someStr}}</div>
中的{{.name}}
和{{.someStr}}
被替換成了 zeta
和這是一個(gè)開始
。并且,不再使用fmt.Fprintln
函數(shù)輸出數(shù)據(jù)到Response了index.html
,并寫入一些HTML代碼 (我不是個(gè)好前端)<html><head></head><body> <div>Hello {{.name}}</div> <div>{{.someStr}}</div></body></html>
myWeb
函數(shù)func myWeb(w http.ResponseWriter, r *http.Request) { //t := template.New("index") //t.Parse("<div>Hi,{{.name}},{{.someStr}}<div>") //將上兩句注釋掉,用下面一句 t, _ := template.ParseFiles("./templates/index.html") data := map[string]string{ "name": "zeta", "someStr": "這是一個(gè)開始", } t.Execute(w, data) // fmt.Fprintln(w, "這是一個(gè)開始")}
在運(yùn)行一下看看,頁面按照HTML文件的內(nèi)容輸出了,并且{{.name}}和{{.someStr}}也替換了,對(duì)吧?template
包的核心功能就是將HTML字符串解析暫存起來,然后調(diào)用Execute
的時(shí)候,用數(shù)據(jù)替換掉HTML字符串中的{{}}
里面的內(nèi)容t:=template.New("index")
初始化一個(gè)template對(duì)象變量,然后用調(diào)用t.Parse
函數(shù)解析字符串模版。t.Execute
函數(shù),不僅用數(shù)據(jù)渲染模版,還替代了fmt.Fprintln
函數(shù)的工作,將輸出到Response數(shù)據(jù)流寫入器中。template
包的ParseFiles
函數(shù),直接解析相對(duì)路徑下的index.html文件并創(chuàng)建對(duì)象變量。map
類型 和 賦值給“_
”map
的初始化可以使用make
:var data = make(map[string]string)data = map[string]string{}
make是內(nèi)置函數(shù),只能用來初始化 map、slice 和 chan,并且make函數(shù)和另一個(gè)內(nèi)置函數(shù)new不同點(diǎn)在于,它返回的并不是指針,而只是一個(gè)類型。map賦值于其他語言的字典對(duì)象相同,取值有兩種方式,請(qǐng)看下面的代碼:
data["name"]="zeta" //賦值name := data["name"] //方式1.普通取值name,ok := data["name"] //方式2.如果不存在name鍵,ok為false
代碼中的變量ok,可以用來判斷這一項(xiàng)是否設(shè)置過,取值時(shí)如果項(xiàng)不存在,是不會(huì)異常的,取出來的值為該類型的零值,比如 int類型的值,不存在的項(xiàng)就為0;string類型的值不存在就為空字符串,所以通過值是否為0值是不能判斷該項(xiàng)是否設(shè)置過的。Go中的map還有幾個(gè)特點(diǎn)需要了解:
ok,會(huì)獲得true 或者 false,判斷該項(xiàng)是否設(shè)置過,true為存在,false為不存在于map中。
map
的項(xiàng)的順序是不固定的,每次遍歷排列的順序都是不同的,所以不能用順序判斷內(nèi)容map
可以用for...range
遍歷map
在函數(shù)參數(shù)中是引用傳遞(Go語言中,只有map、slice、chan是引用傳遞,其他都是值傳遞)_
" 就是用來解決這個(gè)問題的,_
用來丟棄函數(shù)的返回值。比如本例中,template.ParseFiles("./templates/index.html")
除了返回模版對(duì)象外,還會(huì)返回一個(gè)error
對(duì)象,但是這樣簡單的例子,出錯(cuò)的可能性極小,所以我不想處理error
了,將error
返回值用“_
”丟棄掉。注意注意注意:在實(shí)際項(xiàng)目中,請(qǐng)不要丟棄error,任何意外都是可能出現(xiàn)的,丟棄error會(huì)導(dǎo)致當(dāng)出現(xiàn)罕見的意外情況時(shí),非常難于Debug。所有的error都應(yīng)該要處理,至少寫入到日志或打印到控制臺(tái)。(切記,不要丟棄 error ,很多Gopher們?cè)谶@個(gè)問題上有大把的血淚史)OK,到目前為止,用Go語言搭建一個(gè)簡單的網(wǎng)頁的核心部分就完成了。
func main() { http.HandleFunc("/", myWeb) //指定相對(duì)路徑./static 為文件服務(wù)路徑 staticHandle := http.FileServer(http.Dir("./static")) //將/js/路徑下的請(qǐng)求匹配到 ./static/js/下 http.Handle("/js/", staticHandle) fmt.Println("服務(wù)器即將開啟,訪問地址 http://localhost:8080") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Println("服務(wù)器開啟錯(cuò)誤: ", err) }}
在項(xiàng)目的根目錄下創(chuàng)建static目錄,進(jìn)入static目錄,創(chuàng)建js目錄,然后在js目錄里創(chuàng)建一個(gè)index.js文件。alert("Javascript running...");
打開之前的index.html文件,在</body>后面加上 <script src="/js/index.js"></script>
$ go run main.go
,訪問 http://localhost:8080,頁面會(huì)彈出提示框。<script src="/js/index.js"></script>
瀏覽器會(huì)請(qǐng)求 /js/index.js
這個(gè)路徑/js/
,于是用文件服務(wù)處理這次請(qǐng)求,匹配到程序運(yùn)行的路徑下相對(duì)路徑./static/js
。main.go
文件中這兩句//指定相對(duì)路徑./static 為文件服務(wù)路徑 staticHandle := http.FileServer(http.Dir("./static")) //將/js/路徑下的請(qǐng)求匹配到 ./static/js/下 http.Handle("/js/", staticHandle)
也可以寫成一句,更容易理解//瀏覽器訪問/js/ 將會(huì)以靜態(tài)文件形式訪問目錄 ./static/jshttp.Handle("/js/", http.FileServer(http.Dir("./static")))
很簡單...但是,可能還是不滿足需求,因?yàn)? 如果http.Handle("/js/", http.FileServer(http.Dir("./static")))
對(duì)應(yīng)到 ./static/jshttp.Handle("/css/", http.FileServer(http.Dir("./static")))
對(duì)應(yīng)到 ./static/csshttp.Handle("/img/", http.FileServer(http.Dir("./static")))
對(duì)應(yīng)到 ./static/imghttp.Handle("/upload/", http.FileServer(http.Dir("./static")))
對(duì)應(yīng)到 ./static/uploadhttp.StripPrefix
剝開前綴,如下://http.Handle("/js/", http.FileServer(http.Dir("./static"))) //加上http.StripPrefix 改為 : http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./static"))))
這樣,瀏覽器中訪問/js/時(shí),直接對(duì)應(yīng)到./static目錄下,不需要再加一個(gè)/js/子目錄。http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./js"))))
對(duì)應(yīng)到 ./jshttp.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./css"))))
對(duì)應(yīng)到 ./csshttp.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./img"))))
對(duì)應(yīng)到 ./imghttp.Handle("/upload/", http.StripPrefix("/upload/", http.FileServer(http.Dir("./upload"))))
對(duì)應(yīng)到 ./uploadgo run
命令運(yùn)行程序。go run
都會(huì)重新編譯源碼,如何將程序運(yùn)行在沒有Go環(huán)境的計(jì)算機(jī)上?go build
命令,它會(huì)編譯源碼,生成可執(zhí)行的二進(jìn)制文件。go build
命令什么參數(shù)都不用加,它會(huì)自動(dòng)查找目錄下的main包下的main()函數(shù),然后依次查找依賴包編譯成一個(gè)可執(zhí)行文件。go build
會(huì)編譯為和開發(fā)操作系統(tǒng)對(duì)應(yīng)的可執(zhí)行文件,如果要編譯其他操作系統(tǒng)的可執(zhí)行文件,需要用到交叉編譯。GOOS=windows GOARCH=amd64 go build
SET GOOS=linuxSET GOARCH=amd64go build main.go
關(guān)鍵詞:依賴,語言
客戶&案例
營銷資訊
關(guān)于我們
微信公眾號(hào)
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。