<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ### 請求的結構 HTTP的交互以請求和響應的應答模式。go的請求我們早就見過了,handler函數的第二個參數http.Requests。其結構為: ~~~ type Request struct { Method string URL *url.URL Proto string // "HTTP/1.0" ProtoMajor int // 1 ProtoMinor int // 0 Header Header Body io.ReadCloser ContentLength int64 TransferEncoding []string Close bool Host string Form url.Values PostForm url.Values MultipartForm *multipart.Form .... ctx context.Context } ~~~ 從request結構可以看到,http請求的基本信息都囊括了。對于請求而言,主要關注一下請求的URL,Method,Header,Body這些結構。 ### URL HTTP的url請求格式為`scheme://[userinfo@]host/path[?query][#fragment]`, go的提供了一個URL結構,用來映射HTTP的請求URL。 ~~~ type URL struct { Scheme string Opaque string User *Userinfo Host string Path string RawQuery string Fragment string } ~~~ URL的格式比較明確,其實更好的名詞應該是URI,統一資源定位。url中比較重要的是查詢字符串query。通常作為get請求的參數。query是一些使用`&`符號分割的`key1=value1&key2=value2`鍵值對,由于url編碼是ASSIC碼,因此query需要進行urlencode。go可以通過request.URI.RawQuery讀取query ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { info := fmt.Sprintln("URL", r.URL, "HOST", r.Host, "Method", r.Method, "RequestURL", r.RequestURI, "RawQuery", r.URL.RawQuery) fmt.Fprintln(w, info) } ? ~ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=vanyar&age=27' "http://127.0.0.1:8000?lang=zh&version=1.1.0" URL /?lang=zh&version=1.1.0 HOST 127.0.0.1:8000 Method POST RequestURL /?lang=zh&version=1.1.0 RawQuery lang=zh&version=1.1.0 ~~~ ### header header也是HTTP中重要的組成部分。Request結構中就有Header結構,Header本質上是一個map(`map[string][]string`)。將http協議的header的key-value進行映射成一個圖: ~~~ Host: example.com accept-encoding: gzip, deflate Accept-Language: en-us fOO: Bar foo: two Header = map[string][]string{ "Accept-Encoding": {"gzip, deflate"}, "Accept-Language": {"en-us"}, "Foo": {"Bar", "two"}, } ~~~ header中的字段包含了很多通信的設置,很多時候請求都需要指定`Content-Type`。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { info := fmt.Sprintln(r.Header.Get("Content-Type")) fmt.Fprintln(w, info) } ? ~ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=vanyar&age=27' "http://127.0.0.1:8000?lang=zh&version=1.1.0" application/x-www-form-urlencoded ~~~ > Golng 提供了不少打印函數,基本上分為三類三種。即 `Print` `Println` 和`Printf`。 > `Print`比較簡單,打印輸出到標準輸出流,`Println`則也一樣不同在于多打印一個換行符。至于`Printf`則是打印格式化字符串,三個方法都返回打印的bytes數。`Sprint`,`Sprinln`和`Sprintf`則返回打印的字符串,不會輸出到標準流中。`Fprint`,`Fprintf`和`Fprinln`則把輸出的結果打印輸出到`io.Writer`接口中,http中則是`http.ReponseWriter`這個對象中,返回打印的bytes數。 ### Body http中數據通信,主要通過body傳輸。go把body封裝成Request的Body,它是一個ReadCloser接口。接口方法Reader也是一個接口,后者有一個`Read(p []byte) (n int, err error)`方法,因此body可以通過讀取byte數組獲取請求的數據。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { info := fmt.Sprintln(r.Header.Get("Content-Type")) len := r.ContentLength body := make([]byte, len) r.Body.Read(body) fmt.Fprintln(w, info, string(body)) } ? ~ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=vanyar&age=27' "http://127.0.0.1:8000?lang=zh&version=1.1.0" application/x-www-form-urlencoded name=vanyar&age=27 ~~~ 可見,當請求的content-type為application/x-www-form-urlencoded, body也是和query一樣的格式,key-value的鍵值對。換成json的請求方式則如下: ~~~ ? ~ curl -X POST -H "Content-Type: application/json" -d '{name: "vanyar", age: 27}' "http://127.0.0.1:8000?lang=zh&version=1.1.0" application/json {name: "vanyar", age: 27} ~~~ multipart/form-data的格式用來上傳圖片,請求的body如下: ~~~ ? ~ curl -X POST -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "name=vanyar" -F "age=27" "http://127.0.0.1:8000?lang=zh&version=1.1.0" multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW; boundary=------------------------d07972c7800e4c23 --------------------------d07972c7800e4c23 Content-Disposition: form-data; name="name" vanyar --------------------------d07972c7800e4c23 Content-Disposition: form-data; name="age" 27 --------------------------d07972c7800e4c23-- ~~~ ### 表單 解析body可以讀取客戶端請求的數據。而這個數據是無論是鍵值對還是form-data數據,都比較原始。直接讀取解析還是挺麻煩的。這些body數據通常也是表單提供。因此go提供處理這些表單數據的方法。 #### Form go提供了ParseForm方法用來解析表單提供的數據,即content-type 為 x-www-form-urlencode的數據。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { contentType := fmt.Sprintln(r.Header.Get("Content-Type")) r.ParseForm() fromData := fmt.Sprintf("%#v", r.Form) fmt.Fprintf(w, contentType, fromData) } ? ~ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=vanyar&age=27' "http://127.0.0.1:8000?lang=zh&version=1.1.0" application/x-www-form-urlencoded %!(EXTRA string=url.Values{"name":[]string{"vanyar"}, "age":[]string{"27"}, "lang":[]string{"zh"}, "version":[]string{"1.1.0"}})% ~~~ 用來讀取數據的結構和方法大致有下面幾個: ~~~ fmt.Println(r.Form["lang"]) fmt.Println(r.PostForm["lang"]) fmt.Println(r.FormValue("lang")) fmt.Println(r.PostFormValue("lang")) ~~~ 其中`r.Form`和`r.PostForm`必須在調用`ParseForm`之后,才會有數據,否則則是空數組。 而`r.FormValue`和`r.PostFormValue("lang")`無需`ParseForm`的調用就能讀取數據。 此外r.Form和r.PostForm都是數組結構,對于body和url都存在的同名參數,r.Form會有兩個值,即 \["en", "zh"\],而帶`POST`前綴的數組和方法,都只能讀取body的數據。 ~~~ ? ~ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'name=vanyar&age=27&lang=en' "http://127.0.0.1:8000?lang=zh&version=1.1.0" application/x-www-form-urlencoded %!(EXTRA string=url.Values{"version":[]string{"1.1.0"}, "name":[]string{"vanyar"}, "age":[]string{"27"}, "lang":[]string{"en", "zh"}})% ~~~ 此時可以看到,lang參數不僅url的query提供了,post的body也提供了,go默認以body的數據優先,兩者的數據都有,并不會覆蓋。 如果不想讀取url的參數,調用`PostForm`或`PostFormValue`讀取字段的值即可。 ~~~ r.PostForm["lang"][0] r.PostFormValue["lang"] ~~~ 對于form-data的格式的數據,ParseForm的方法只會解析url中的參數,并不會解析body中的參數。 ~~~ ? ~ curl -X POST -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "name=vanyar" -F "age=27" -F "lang=en" "http://127.0.0.1:8000?lang=zh&version=1.1.0" multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW; boundary=------------------------5f87d5bfa764488d %!(EXTRA string=url.Values{"lang":[]string{"zh"}, "version":[]string{"1.1.0"}} )% ~~~ 因此當請求的content-type為form-data的時候,ParseFrom則需要改成 MutilpartFrom,否則r.From是讀取不到body的內容,只能讀取到query string中的內容。 #### MutilpartFrom ParseMutilpartFrom方法需要提供一個讀取數據長度的參數,然后使用同樣的方法讀取表單數據,MutilpartFrom只會讀取body的數據,不會讀取url的query數據。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { r.ParseMultipartForm(1024) fmt.Println(r.Form["lang"]) fmt.Println(r.PostForm["lang"]) fmt.Println(r.FormValue("lang")) fmt.Println(r.PostFormValue("lang")) fmt.Println(r.MultipartForm.Value["lang"]) fmt.Fprintln(w, r.MultipartForm.Value) } ~~~ 可以看到請求之后返回 `map[name:[vanyar] age:[27] lang:[en]]`。即`r.MultipartForm.Value`并沒有url中的參數。 總結一下,讀取urlencode的編碼方式,只需要ParseForm即可,讀取form-data編碼需要使用ParseMultipartForm方法。如果參數中既有url,又有body,From和FromValue方法都能讀取。而帶`Post`前綴的方法,只能讀取body的數據內容。其中MultipartForm的數據通過`r.MultipartForm.Value`訪問得到。 ### 文件上傳 form-data格式用得最多方式就是在圖片上傳的時候。`r.MultipartForm.Value`是post的body字段數據,`r.MultipartForm.File`則包含了圖片數據: ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { r.ParseMultipartForm(1024) fileHeader := r.MultipartForm.File["file"][0] fmt.Println(fileHeader) file, err := fileHeader.Open() if err == nil{ data, err := ioutil.ReadAll(file) if err == nil{ fmt.Println(len(data)) fmt.Fprintln(w, string(data)) } } fmt.Println(err) } ~~~ 發出請求之后,可以看見返回了圖片。當然,go提供了更好的工具函數`r.FormFile`,直接讀取上傳文件數據。而不需要再使用 ParseMultipartForm 方法。 ~~~ file, _, err := r.FormFile("file") if err == nil{ data, err := ioutil.ReadAll(file) if err == nil{ fmt.Println(len(data)) fmt.Fprintln(w, string(data)) } } fmt.Println(err) ~~~ 這種情況只適用于出了文件字段沒有其他字段的時候,如果仍然需要讀取lang參數,還是需要加上 ParseMultipartForm 調用的。讀取到了上傳文件,接下來就是很普通的寫文件的io操作了。 ### JSON 現在流行前后端分離,客戶端興起了一些框架,angular,vue,react等提交的數據,通常習慣為json的格式。對于json格式,body就是原生的json字串。也就是go解密json為go的數據結構。 ~~~ type Person struct { Name string Age int } func indexHandler(w http.ResponseWriter, r *http.Request) { decode := json.NewDecoder(r.Body) var p Person err := decode.Decode(&p) if err != nil{ log.Fatalln(err) } info := fmt.Sprintf("%T\n%#v\n", p, p) fmt.Fprintln(w, info) } ? ~ curl -X POST -H "Content-Type: application/json" -d '{"name": "vanyar", "age": 27 }' "http://127.0.0.1:8000?lang=zh&version=1.1.0" main.Person main.Person{Name:"vanyar", Age:27} ~~~ 更多關于json的細節,以后再做討論。訪問官網[文檔](https://link.jianshu.com?t=https://blog.golang.org/json-and-go)獲取更多的信息。 ### Response 請求和響應是http的孿生兄弟,不僅它們的報文格式類似,相關的處理和構造也類似。go構造響應的結構是`ResponseWriter`接口。 ~~~ type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(int) } ~~~ 里面的方法也很簡單,`Header`方法返回一個header的map結構。`WriteHeader`則會返回響應的狀態碼。`Write`返回給客戶端的數據。 我們已經使用了fmt.Fprintln 方法,直接向w寫入響應的數據。也可以調用Write方法返回的字符。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { str := `<html> <head><title>Go Web Programming</title></head> <body><h1>Hello World</h1></body> </html>` w.Write([]byte(str)) } ? ~ curl -i http://127.0.0.1:8000/ HTTP/1.1 200 OK Date: Wed, 07 Dec 2016 09:13:04 GMT Content-Length: 95 Content-Type: text/html; charset=utf-8 <html> <head><title>Go Web Programming</title></head> <body><h1>Hello World</h1></body> </html>% ? ~ ~~~ go根據返回的字符,自動修改成了`text/html`的`Content-Type`格式。返回數據自定義通常需要修改header相關信息。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(501) fmt.Fprintln(w, "No such service, try next door") } ? ~ curl -i http://127.0.0.1:8000/ HTTP/1.1 501 Not Implemented Date: Wed, 07 Dec 2016 09:14:58 GMT Content-Length: 31 Content-Type: text/plain; charset=utf-8 No such service, try next door ~~~ #### 重定向 重定向的功能可以更加設置header的location和http狀態碼實現。 ~~~ func indexHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", "https://google.com") w.WriteHeader(302) } ? ~ curl -i http://127.0.0.1:8000/ HTTP/1.1 302 Found Location: https://google.com Date: Wed, 07 Dec 2016 09:20:19 GMT Content-Length: 31 Content-Type: text/plain; charset=utf-8 ~~~ 重定向是常用的功能,因此go也提供了工具方法,`http.Redirect(w, r, "https://google.com", http.StatusFound)`。 與請求的Header結構一樣,w.Header也有幾個方法用來設置headers ~~~ func (h Header) Add(key, value string) { textproto.MIMEHeader(h).Add(key, value) } func (h Header) Set(key, value string) { textproto.MIMEHeader(h).Set(key, value) } func (h MIMEHeader) Add(key, value string) { key = CanonicalMIMEHeaderKey(key) h[key] = append(h[key], value) } func (h MIMEHeader) Set(key, value string) { h[CanonicalMIMEHeaderKey(key)] = []string{value} } ~~~ Set和Add方法都可以設置headers,對于已經存在的key,Add會追加一個值value的數組中,,set則是直接替換value的值。即 append和賦值的差別。 ### Json 請求發送的數據可以是JSON,同樣響應的數據也可以是json。restful風格的api也是返回json格式的數據。對于請求是解碼json字串,響應則是編碼json字串,go提供了標準庫 `encoding/json` ~~~ type Post struct { User string Threads []string } func indexHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") post := &Post{ User: "vanyar", Threads: []string{"first", "second", "third"}, } json, _ := json.Marshal(post) w.Write(json) } ? ~ curl -i http://127.0.0.1:8000/ HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 08 Dec 2016 06:45:17 GMT Content-Length: 54 {"User":"vanyar","Threads":["first","second","third"]}% ~~~ 當然,更多的json處理細節稍后再做介紹。 ### 總結 對于web應用程式,處理請求,返回響應是基本的內容。golang很好的封裝了Request和ReponseWriter給開發者。無論是請求還是響應,都是針對url,header和body相關數據的處理。也是http協議的基本內容。 除了body的數據處理,有時候也需要處理header中的數據,一個常見的例子就是處理cookie。這將會在cookie的話題中討論。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看