GO WBE学习笔记
GoWeb學習筆記
學習的資料來自楊旭老師在B站的視頻
文章目錄
- GoWeb學習筆記
- 創建第一個Web程序(網頁輸出HelloWorld)
- HadleFunc源碼
- 使用HadleFunc,并創建內置函數的形式創建訪問函數
- 使用HadleFunc,并調用外部函數的形式創建訪問函數
- 創建訪問監聽和服務
- http.ListenAndServe源碼
- http.Server源碼(去除了源碼中的注釋)
- 使用http.ListenAndServe創建監聽和服務
- 使用http.Server創建監聽和服務
- 整合實現
- 直接創建
- 通過外部函數和使用http.Server實現
- http.Handle源碼
- 使用http.Handle創建一個Hello World
- 注意
- Go語言的五個內置Handler
- HTTP消息
- Request(請求)
- FORM字段
- 上傳文件
- POST請求-JSON BODY
- ResponseWriter
- 內置的Response
- 內置的Response
創建第一個Web程序(網頁輸出HelloWorld)
在go語言中創建一個網頁中輸出的HelloWorld,需要先創建一個訪問的函數,然后指定相應的監聽和服務
HadleFunc源碼
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler) }使用HadleFunc,并創建內置函數的形式創建訪問函數
//HandleFunc一共有兩個參數,第一個參數是訪問路徑,第二個參數是路由函數 http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {writer.Write([]byte("Hello World!")) })使用HadleFunc,并調用外部函數的形式創建訪問函數
//自定義的外部函數 func MyHandleFunc(w http.ResponseWriter,r *http.Request){w.Write([]byte("MyHandleFunc")) }func main(){//調用函數http.HandleFunc("/myHandleFunc",MyHandleFunc) }創建訪問監聽和服務
創建訪問監聽和服務有兩種方式,一個是調用http.ListenAndServe方法,需要配置兩個參數,另一個是調用http.Server,這種方式需要自定http.Server提供的參數,相對于http.ListenAndServe,這種方式更加靈活
http.ListenAndServe源碼
func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe() }http.Server源碼(去除了源碼中的注釋)
type Server struct {Addr stringHandler Handler TLSConfig *tls.ConfigReadTimeout time.DurationReadHeaderTimeout time.DurationWriteTimeout time.DurationIdleTimeout time.DurationMaxHeaderBytes intTLSNextProto map[string]func(*Server, *tls.Conn, Handler)ConnState func(net.Conn, ConnState)ErrorLog *log.LoggerBaseContext func(net.Listener) context.ContextConnContext func(ctx context.Context, c net.Conn) context.ContextinShutdown atomicBool disableKeepAlives int32 nextProtoOnce sync.Once nextProtoErr error mu sync.Mutexlisteners map[*net.Listener]struct{}activeConn map[*conn]struct{}doneChan chan struct{}onShutdown []func() }使用http.ListenAndServe創建監聽和服務
//使用nil相當與使用了go語言內置的http.DefaultServeMux(多路復用器) //需要傳入兩個參數,分別是訪問的地址和使用的訪問函數 //當使用localhost的時候可以寫為http.ListenAndServe(":8080",nil) http.ListenAndServe("localhost:8080",nil)使用http.Server創建監聽和服務
//等同于http.ListenAndServe,但是使用這種方式配置更加靈活,因為可以設置更多參數值server := http.Server{Addr: "localhost:8080",Handler: nil,}server.ListenAndServe()整合實現
直接創建
//訪問路徑為localhost:8080 http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {writer.Write([]byte("Hello World!")) }) http.ListenAndServe("localhost:8080",nil)通過外部函數和使用http.Server實現
//為了方便,寫了一個監聽的函數 func Listen(){server := http.Server{Addr: "localhost:8080",Handler: nil,}server.ListenAndServe() } //自定義的訪問函數 func MyHandleFunc(w http.ResponseWriter,r *http.Request){//因為Write()函數的源碼為Write([]byte) (int, error),所以在輸出string類型的時候需要轉換w.Write([]byte("Hello World")) } func main(){http.HandleFunc("/myHandleFunc",MyHandleFunc)Listen() }http.Handle源碼
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }使用http.Handle創建一個Hello World
有http.Handle的源碼可以看出傳入的第二個參數為Handler,源碼如下:
type Handler interface {ServeHTTP(ResponseWriter, *Request) }Handler是一個ServeHTTP類型的Type,所以傳入Handle的第二個參數也得是ServeHTTP類型的訪問函數
//自定義路由 type HelloHandler struct {}//自定義路由 type AboutHandler struct {} //定義訪問控制器 func (m *HelloHandler) ServeHTTP(w http.ResponseWriter ,r *http.Request) {w.Write([]byte("Hello World!")) } //定義訪問控制器 func (m *AboutHandler) ServeHTTP(w http.ResponseWriter ,r *http.Request) {w.Write([]byte("About!")) } func MyListen(){mh := HelloHandler{}ma := AboutHandler{}server := http.Server{Addr: ":8080",//不指定訪問路由器,從而達成通過訪問不同的路由參數而訪問不同的自定義路由器Handler: nil,}http.Handle("/hello",&mh)http.Handle("/about",&ma)server.ListenAndServe() }注意
在上面使用Handle函數的時候是使用創建好的ServeHttp,我們也可以使用HandlerFunc來將創建的沒有繼承ServeHTTP的 路由轉化
//使用HandlerFunc將自定義的訪問控制轉換為一個Handler //HandlerFunc可以將某個具有適當簽名的函數f適配成為一個Handler http.Handle("/my", http.HandlerFunc(MyHandleFunc))func MyHandleFunc(w http.ResponseWriter, r *http.Request) {w.Write([]byte("MyHandleFunc")) }Go語言的五個內置Handler
1.NotFoundHandler
返回一個handler,每個請求的相應都是404 page not found
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }2.RedirectHandler
返回一個handler,把每一個請求按照狀態碼跳轉到指定的URL
常見的:StatusMovedPermanently、StatusFound、StatusSeeOther
func RedirectHandler(url string, code int) Handler {return &redirectHandler{url, code} }3.StripPrefix
去前綴,返回一個handler,在指定的url中去掉前綴,然后調用另一個handler
func StripPrefix(prefix string, h Handler) Handler {if prefix == "" {return h}return HandlerFunc(func(w ResponseWriter, r *Request) {if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {r2 := new(Request)*r2 = *rr2.URL = new(url.URL)*r2.URL = *r.URLr2.URL.Path = ph.ServeHTTP(w, r2)} else {NotFound(w, r)}}) }4.TimeoutHandler
返回一個handler,在指定時間內運行傳入的handler
func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler {return &timeoutHandler{handler: h,body: msg,dt: dt,} }5.FileServer
返回一個handler,使用基于root的文件系統來相應請求
func FileServer(root FileSystem) Handler {return &fileHandler{root} } type FileSystem interface {Open(name string) (File, error) }在使用時需要用到操作系統的文件系統,所以一般交給下面的函數來用
type Dir string func (d Dir) Open(name string) (File, error) {if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) {return nil, errors.New("http: invalid character in file path")}dir := string(d)if dir == "" {dir = "."}fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))f, err := os.Open(fullName)if err != nil {return nil, mapDirOpenError(err, fullName)}return f, nil }HTTP消息
HTTP Request 和HTTP Response,他倆具有相同的結構,都有請求行,0個或者多個url,空行以及可選的消息體(body)
Request(請求)
在GO語言中Request是一個struct,代表了客戶端發送的HTTP請求消息(既可以代表客戶端的請求,也可以代表服務端的請求),可以通過Request的方法訪問請求中的Cookie、URL、User Agent等信息,源碼如下:
type Request struct {Method stringURL *url.URLProto string // "HTTP/1.0"ProtoMajor int // 1ProtoMinor int // 0Header HeaderBody io.ReadCloserGetBody func() (io.ReadCloser, error)ContentLength int64TransferEncoding []stringClose boolHost stringForm url.ValuesPostForm url.ValuesMultipartForm *multipart.FormTrailer HeaderRemoteAddr stringRequestURI stringTLS *tls.ConnectionStateCancel <-chan struct{}Response *Responsectx context.Context }其中幾個重要的字段
1.URL
Request的URL字段就代表了請求行(請求信息第一行)里面的部分內容,URL字段是指向url.URL類型的一個指針,url.URL是一個struct源碼如下:
type URL struct {Scheme stringOpaque string User *Userinfo Host string Path string RawPath string ForceQuery bool RawQuery string Fragment string RawFragment string }URL的通用格式為:
scheme://[userinfo@]host/path[?query][#fragment]不以斜杠開頭的URL被解釋為:
scheme:opaque[?query][#fragment]URL Query
- RawQuery提供實際查詢的字符串
- 通過Request的Form字段
r.URL.Query()
該方法會提供查詢字符串對應的map[string] [] string
URL Fragment
就是URL格式中的#后面的部分
當請求從瀏覽器發出時,無法獲取到Fragment的值,因為在瀏覽器發送請求的時候會把Fragment去掉
部分客戶端工具發出的請求可以獲取到Fragment的值,例如HTTP客戶端包
2.Handler
請求和相應的headers是通過Header類型來描述的,它是一個map類型,用來描述HTTP header里的Key-Value對。
Header map的key是string類型,value是[]string
設置key時會創建一個空的[]string作為value,value里面第一個元素就是新的header的值
如果是為指定的key添加一個新的header值的話,執行append操作即可
3.Body
請求和相應的bodies都是使用Body字段來表示的
Body是一個io.ReadCloser接口,一個Reader接口和一個Closer接口
Reader接口定義了一個Open方法,參數[]byte,返回byte的數量、可選的錯誤
Closer接口定義了一個Close方法:沒有參數,返回可選的錯誤
讀取body的內容,調用Body的Read方法
func getQuery(){http.HandleFunc("/query", func(writer http.ResponseWriter, request *http.Request) {//獲取URLurl := request.URL//調用query方法query := url.Query()//根據傳入的key值查詢相應的數據,返回全部值id := query["id"]//以日志的形式打印在控制臺log.Println(id)//根據傳入的key值返回第一個值name := query.Get("name")log.Println(name)}) }4.Form、PostForm、MultipartForm
通過表單發送post請求
<form action="/index" method="post">用戶名<input type="text" name="name" />密碼<input type="password" name="password" /><input type="submit" /> </form>action是發送請求對應的服務器路徑,method是發送請求的方式,有post和get兩種,html表單里的數據會以name-value對的方式通過post請求發送出去。
name-value
通過psot發送的name-value數據對的格式通過表單的Content Type來指定,也就是表單里面的enctype屬性,在form表單中enctype的默認屬性為application/x-www-form-urlencoded。
1.application/x-www-form-urlencoded
在這個屬性下,瀏覽器將會將表單數據編碼到查詢字符串里面,簡單的文本格式使用這種方式
2.multipart/form-data
在這種屬性下每一個name-value對都會被轉化為一個mime消息部分
每一個部分都有自己的Content Type和Content Disposition,在上傳文件的時候選用這個方式
3.text/plain
POST & GET
表單的method屬性可以設置post和get兩種屬性
1.GET
get請求沒有body,所有的數據都通過URL的name-value對來發送
2.POST
FORM字段
Resquest上的函數允許我們通過url或/和body中提取數據,form里面的數據是key-value對
通常的做法是先調用ParseForm或ParseMultipartForm來解析Request,然后相應的訪問Form、PostForm或MultipartForm字段
PostForm
PostForm只支持application/x-www-form-urlencoded,
func getForm(){http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) {request.ParseForm()//輸出到頁面fmt.Fprintln(writer,request.Form)//以日志的形式打印到控制臺log.Println(request.Form)}) }前端代碼:
<form action="http://localhost:8080/process" method="post" >用戶名<input type="text" name="name" />密碼<input type="password" name="password" /><input type="submit" /> </form>MultipartForm
使用MultipartForm的時候需要先調用ParseMultipartForm,ParseMultipartForm會在必要的時候調用ParseForm,里面需要傳入一個參數(讀取的數據長度,單位為字節),MultipartForm只包含表單的key-value對,返回類型是一個struct而不是map,這個struct里面包含兩個map,一個是你表單里面的數據,另一個是文件
func getMultipart(){http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) {//參數為字節,是上傳數據的長度request.ParseMultipartForm(1024)fmt.Fprintln(writer,request.MultipartForm)log.Println(request.MultipartForm)}) }FormValue&PostFormValue
FormValue方法會返回form字段指定key對應的第一個value,無需調用ParseForm和ParseMultipartForm
PostFormValue方法只能讀取PostForm
這兩種該方法都會調用ParseMultipartForm
當你表單的enctype設置為multipart/form-data的時候,上面兩種方法無法獲取到表單的數據
上傳文件
首先form里面的enctype類型要設置為multipart/form-data
在GO語言中處理上傳文件的時候:
1.調用ParseMultiparForm方法
2.從file字段獲得FileHeadler,調用Open方法來獲得文件
3.可以使用ioutil.ReadAll函數將文件內容讀取到byte切片里
func getFile(){http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) {request.ParseMultipartForm(1024)//因為file是一個map,也就是允許多個文件上傳,這里可以指定獲取那個文件,0代表第一個fileHeader := request.MultipartForm.File["uploaded"][0]file,err := fileHeader.Open()if err == nil {data,err := ioutil.ReadAll(file)if err != nil {fmt.Println(err)}fmt.Fprintln(writer,string(data))}}) }方法2
func getFile2(){http.HandleFunc("/process", func(writer http.ResponseWriter, request *http.Request) {//返回第一個文件,當只上傳一個文件的時候,這種方式更快file,_,err := request.FormFile("uploaded")if err == nil {data,err := ioutil.ReadAll(file)if err != nil {fmt.Println(err)}fmt.Fprintln(writer,string(data))}}) }MultipartReader()
源碼如下:
func (r *Request) MultipartReader() (*multipart.Reader, error) {if r.MultipartForm == multipartByReader {return nil, errors.New("http: MultipartReader called twice")}if r.MultipartForm != nil {return nil, errors.New("http: multipart handled by ParseMultipartForm")}r.MultipartForm = multipartByReader如果是multipart/form-data或multipart混合的POST請求,MultipartReader會返回一個MIME multipart reader,否則這返回一個error和nil
在使用中可以使用MultipartReader代替ParseMultipartForm來把請求的body作為stream進行處理,它在處理的時候不是一次性處理整個表單數據,而是檢查來自表單的值,然后每次處理一個
POST請求-JSON BODY
不是所有的post請求都來自form
在不同的客戶端框架下會以不同的方式對post請求編碼
例如jQuery通常使用application/x-www-form-urlencoded
Augular則是application/json,但是ParseForm方法無法處理application/json
ResponseWriter
從服務器向客戶端返回相應需要使用ResponseWriter
ResponseWriter是一個接口,handler用它來返回相應,真正支撐ResponseWriter的幕后struct是一個非導出的http.response
寫入到ResponseWriter
ResponseWriter在底層實現的時候其實也是實現了一個指針
在ResponseWriter中,write方法接收一個byte切片作為參數,然后把他寫入到HTTP相應的Body里面。
如果Write方法被調用時,header里面沒有設定content type,那么數據的前512字節就會被用來監測content type
func writeExample(w http.ResponseWriter,r *http.Request) {str := `<html><head><title>Go Web</title></head> <body><h1>Hello World</h1></body></html>`w.Write([]byte(str)) }WriteHeader
WriteHeader方法接收一個整數類型(HTTP狀態碼)作為參數,并把它作為HTTP響應的狀態碼返回,如果這個方法沒有被顯示的調用,那么在第一次調用Write方法前會隱式的調用
當WriteHeader被調用完后,仍然可以寫入到ResponseWriter,但是不能再修改header
func writeHeader(w http.ResponseWriter,r *http.Request) {w.WriteHeader(501)fmt.Fprintln(w,"66666666666666") }Header
Header方法返回Headers的map。可以進行修改,修改后的headers將會體現再返回給客戶端的HTTP響應里
func headerExample(w http.ResponseWriter,r * http.Request) {//重定向訪問的請求w.Header().Set("location","http://www.baidu.com")//訪問請求的狀態碼w.WriteHeader(302) }傳入json數據
type Post struct {User stringThreads []string }func jsonExample(w http.ResponseWriter , r *http.Request) {w.Header().Set("Content-Type","application/json")post := &Post{User: "張三",Threads: []string{"666","777","888"},}json,_:= json2.Marshal(post)w.Write(json) }內置的Response
1.NotFound函數,包裝一個404狀態碼和一個額外的信息
2.ServeFile函數,從文件系統提供文件,返回給請求者
3.ServeContent函數,它可以把實現了io.ReadSeeker接口的任何東西里面的內容返回給請求者,同時它還可以處理Range請求(范圍請求),如果只請求了資源的一部分內容,那么ServeContent就可以如此響應。而ServeFile或io.Copy則不行
4.Redirect函數,告訴客戶端重定向到另一個URL
tp://www.baidu.com")
//訪問請求的狀態碼
w.WriteHeader(302)
}
內置的Response
1.NotFound函數,包裝一個404狀態碼和一個額外的信息
2.ServeFile函數,從文件系統提供文件,返回給請求者
3.ServeContent函數,它可以把實現了io.ReadSeeker接口的任何東西里面的內容返回給請求者,同時它還可以處理Range請求(范圍請求),如果只請求了資源的一部分內容,那么ServeContent就可以如此響應。而ServeFile或io.Copy則不行
4.Redirect函數,告訴客戶端重定向到另一個URL
總結
以上是生活随笔為你收集整理的GO WBE学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL优化面试题及答案
- 下一篇: html5 中的 wbe storage