閱讀:《Golang web 應用開發》
一. Web工作方式
上網過程
對於普通的上網過程,系統其實是這樣做的:瀏覽器本身是一個客戶端,當你輸入URL的時候,首先瀏覽器會去請求DNS服務器,通過DNS獲取相應的域名對應的IP,然後通過IP地址找到IP對應的服務器後,要求建立TCP連接,等瀏覽器發送完HTTP Request(請求)包後,服務器接收到請求包之後纔開始處理請求包,服務器調用自身服務,返回HTTP Response(響應)包;客戶端收到來自服務器的響應後開始渲染這個Response包裏的主體(body),等收到全部的內容隨後斷開與該服務器之間的TCP連接。
URL和DNS解析
URL解析格式:
scheme://host[:port#]/path/…/[?query-string][#anchor]
scheme 指定底層使用的協議(例如:http, https, ftp)
host HTTP服務器的IP地址或者域名
port# HTTP服務器的默認端口是80,這種情況下端口號可以省略。如果使用了別的端口,必須指明,例如 http://www.cnblogs.com:8080/
path 訪問資源的路徑
query-string 發送給http服務器的數據
anchor 錨
DNS解析過程
通過上面的步驟,我們最後獲取的是IP地址,也就是瀏覽器最後發起請求的時候是基於IP來和服務器做信息交互的。
HTTP協議
- 在HTTP中,客戶端總是通過建立一個連接與發送一個HTTP請求來發起一個事務
- HTTP協議是無狀態的,同一個客戶端的這次請求和上次請求是沒有對應關係的,對HTTP服務器來說,它並不知道這兩個請求是否來自同一個客戶端。爲了解決這個問題, Web程序引入了Cookie機制來維護連接的可持續狀態。
HTTP請求包(瀏覽器信息)
Request
Request包分爲3部分,第一部分叫Request line(請求行), 第二部分叫Request header(請求頭),第三部分是body(主體)。header和body之間有個空行,請求包的例子所示:
GET /domains/example/ HTTP/1.1 //請求行: 請求方法 請求URI HTTP協議/協議版本
Host:www.iana.org //服務端的主機名
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //瀏覽器信息
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 //客戶端能接收的MIME
Accept-Encoding:gzip,deflate,sdch //是否支持流壓縮
Accept-Charset:UTF-8,*;q=0.5 //客戶端字符編碼集
//空行,用於分割請求頭和消息體
//消息體,請求資源參數,例如POST傳遞的參數
HTTP協議定義了很多與服務器交互的請求方法,最基本的有4種,分別是GET,POST,PUT,DELETE。一個URL地址用於描述一個網絡上的資源,而HTTP中的GET, POST, PUT, DELETE就對應着對這個資源的查,增,改,刪4個操作。我們最常見的就是GET和POST了。GET一般用於獲取/查詢資源信息,而POST一般用於更新資源信息。
HTTP響應包
Respons
HTTP/1.1 200 OK //狀態行
Server: nginx/1.0.8 //服務器使用的WEB軟件名及版本
Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //發送時間
Content-Type: text/html //服務器發送信息的類型
Transfer-Encoding: chunked //表示發送HTTP包是分段發的
Connection: keep-alive //保持連接狀態
Content-Length: 90 //主體內容長度
//空行 用來分割消息頭和主體
!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”… //消息體
二. Go搭建一個Web服務器
http包建立Web服務器
代碼
package main
import (
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析參數,默認是不會解析的
fmt.Println(r.Form) //這些信息是輸出到服務器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") //這個寫入到w的是輸出到客戶端的
}
func main() {
http.HandleFunc("/", sayhelloName) //設置訪問的路由
err := http.ListenAndServe(":9090", nil) //設置監聽的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
執行結果
三. Go創建Web服務器的幾種方式
http.HandleFunc函數
使用HandleFunc函數是http封裝好的一個函數,可以直接使用,第一個參數是web請求路徑,第二個參數是的func(writer http.ResponseWriter, request *http.Request)函數。
再使用http.ListenAndServe(":9090",nil)語句,監聽9090端口,運行程序後。
使用http://localhost:9090,便會輸出Hello astaxie!
其中http.ResponseWriter代表對客戶端的響應體,而http.Request代表客戶端發送服務器的請求數據。
http.Handle函數
跟HandleFunc一樣,Handle也是http封裝好的函數,第一個參數跟HandleFunc一樣,而第二個參數則是必須是實現了http.Handler接口的類型,http.Handler在http包的定義如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
http.ServeMux
無論是使用http.Handle還是http.HandleFunc函數,其實底層代碼都是使用http.DefaultServeMux,DefaultServeMux的定義如下代碼所示:
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
type Controller struct {}
func (c Controller)ServeHTTP(writer http.ResponseWriter, request *http.Request){
writer.Write([]byte("hello,1"));
}
func hello(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("hello,2"));
}
func main(){
mux := &http.ServeMux{}
mux.HandleFunc("/hello1",hello)
mux.Handle("/hello2",http.HandlerFunc(hello))
mux.Handle("/hello3",&Controller{})
log.Fatal(http.ListenAndServe(":8080",mux))
}
http.Server
http.Server是http包中對web更加底層的支持,我們前面使用的方法,都是對http.Server的封裝而已,如果直接使用http.Server,則可以自定義更多的參數,如果連接超時等參數,因此我們下面直接使用http.Server開發Web服務。