Hypertext Transfer Protocol,超⽂本传输协议。是互联⽹应⽤最⼴泛的⽹络协议,定义了客户端和服务端之间请求与响应的传输标准。
HTTP是⼀个基于TCP/IP通信协议来传递数据,服务器传输超⽂本到本地浏览器的传送协议。HTTP协议⼯作于客户端-服务端架构上。浏览器可作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。它是⼀个⽆状态的请求/响应协议。
客户端请求消息和服务器响应消息都会包含请求头和请求体。HTTP请求头提供了关于请求或响应,发送实体的信息,如:Content-Type、ContentLength、Date等。当浏览器接收并显示⽹⻚前,此⽹⻚所在的服务器会返回⼀个包含HTTP状态码的信息头(server header)⽤以响应浏览器的请求。HTTP状态码的英⽂为HTTP Status Code。
下⾯是常⻅的HTTP状态码:
- 200 - 请求成功
- 301 - 资源(⽹⻚等)被永久转移到其它URL
- 404 - 请求的资源(⽹⻚等)不存在
- 413 - 由于请求的实体过⼤,服务器⽆法处理,因此拒绝请求。为防⽌客户端的连续请求,服务器可能会关闭连接。
- 500 - 内部服务器错误
常⽤⽅法: GetPostHead
Http定义了与服务器交互的不同⽅法,最基本的⽅法有4种,分别是**GET,POST,PUT,DELETE,**对应着对这个资源的查 ,改 ,增 ,删 4个操作。
另外还有个Head⽅法. 类似GET⽅法,只请求⻚⾯的⾸部,不响应⻚⾯Body部分,⽤于获取资源的基本信息,即检查链接的可访问性及资源是否修改。
GET和POST的区别
- GET在浏览器回退时是⽆害的,⽽POST会再次提交请求。
- GET产⽣的URL地址可以被Bookmark,⽽POST不可以。
- GET请求会被浏览器主动cache,⽽POST不会,除⾮⼿动设置。
- GET请求只能进⾏url编码,⽽POST⽀持多种编码⽅式。
- GET请求参数会被完整保留在浏览器历史记录⾥,⽽POST中的参数不会被保留。
- GET请求在URL中传送的参数是有⻓度限制的,⽽POST没有。
- 对参数的数据类型,GET只接受ASCII字符,⽽POST没有限制。
- GET⽐POST更不安全,因为参数直接暴露在URL上,所以不能⽤来传递敏感信息。
- GET参数通过URL传递,POST放在Request body中。
HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。 - GET产⽣⼀个TCP数据包;POST产⽣两个TCP数据包。
- 对于GET⽅式的请求,浏览器会把http header和data⼀并发送出去,服务器响应200(返回数据);
- ⽽对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据))
HTTPS(Secure Hypertext Transfer Protocol)安全超⽂本传输协议 它是⼀个安全通信通道
HTTPS是HTTP over SSL/TLS,HTTP是应⽤层协议,TCP是传输层协议,在应⽤层和传输层之间,增加了⼀个安全套接层SSL。
服务器 ⽤RSA⽣成公钥和私钥把公钥放在证书⾥发送给客户端,私钥⾃⼰保存客户端⾸先向⼀个权威的服务器检查证书的合法性,如果证书合法,客户端产⽣⼀段随机数,这个随机数就作为通信的密钥,我们称之为对称密钥,⽤公钥加密这段随机数,然后发送到服务器服务器⽤密钥解密获取对称密钥,然后,双⽅就已对称密钥进⾏加密解密通信了。
- 内容加密 建⽴⼀个信息安全通道,来保证数据传输的安全;
- 身份认证 确认⽹站的真实性
- 数据完整性 防⽌内容被第三⽅冒充或者篡改
Https和Http的区别 - https协议需要到CA申请证书。
- http是超⽂本传输协议,信息是明⽂传输;https 则是具有安全性的ssl加密传输协议。
- http和https使⽤的是完全不同的连接⽅式,⽤的端⼝也不⼀样,前者是80,后者是443。
- http的连接很简单,是⽆状态的;HTTPS协议是由SSL+HTTP协议构建的可进⾏加密传输、身份认证的⽹络协议,⽐http协议安全。
Go语⾔标准库内置了net/http包,涵盖了HTTP客户端和服务端的具体实现。
内置的net/http包,提供了最简洁的HTTP客户端实现,⽆需借助第三⽅⽹络通信库就可以直接使⽤HTTP中⽤的最多的GET和POST⽅式请求数据。
- http.Get()
- http.Post()
- http.PostForm()
- http.Head()
- http.Do()
- ⼀个简单的get实现
func main() {
requestUrl := “http://www.baidu.com”
response, err := http.Get(requestUrl)
if err != nil {
fmt.Println(“err:”,err);
}
defer response.Body.Close();
fmt.Println(response.Body) }
err: Get www.baidu.com: unsupported protocol scheme “”
错误:不⽀持scheme为空的协议,这是什么情况呢,
requestUrl改为requestUrl := “http://www.baidu.com”
scheme反应出的就是协议名称
⼤家可以看到打印的是 &{0xc420124200 }
Body是io.ReadCloser这个类型的对象
response中的参数⼀般也⽐较多,我们需要的最多的通常是Body参数,⽤ioutil.ReadAll去转化io.ReadCloser类型,输出是byty[],再通过string()强制转换就能看到string了
readBody, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(readBody))
咱们这个是正确获取到了服务器响应的数据才能这么打印出来了,加⼊失败了呢,因此需要判断返回码,再去获取body的数据。
if response.StatusCode == 200 {
r, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println(err)
}
return string®
} - ⽅式⼀ 使⽤http.NewRequest
先⽣成http.client -> 再⽣成 http.request -> 之后提交请求:client.Do(request)-> 处理返回结果,每⼀步的过程都可以设置⼀些具体的参数 - ⽅式⼆ 先⽣成client,之后⽤client.get/post…
client结构⾃⼰也有⼀些发送api的⽅法,⽐如client.get,client.post,client.postform…等等。基本上涵盖了主要的http请求的类型,通常不进⾏什么特殊的配置的话,这样就可以了,其实client的get或者post⽅法,也是对http.Newerequest⽅法的封装,⾥⾯还额外添加了
req.Header.Set(“Content-Type”, bodyType)⼀般⽤的话,也是ok的 - ⽅式三 http. Get/Post…
具体实现的时候,还是采⽤的⽅式⼀模式,先⽣成⼀个默认的client,之后调⽤http.Newrequest⽅法。
http.NewRequest
client := &http.Client{}
request, err := http.NewRequest(“GET”, requestUrl, nil)
if err != nil {
fmt.Println(err)
}
cookie := &http.Cookie{Name: “userId”, Value: strconv.Itoa(12345)}
request.AddCookie(cookie) //request中添加cookie
//设置request的header
request.Header.Set(“Content-Type”, “text/html”)
//Content-Length等
response, err := client.Do(request) - Post
postvalue := url.Values{
“theCityName”: {“天津”}, }
body := bytes.NewBufferString(postvalue.Encode())
response, err := http.Post(requestUrl, “application/x-www-form-urlencoded”,
body) //Post⽅法
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println(“PATH: “, r.URL.Path)
fmt.Println(“HOST: “, r.URL.Host)
fmt.Println(“METHOD: “, r.Method)
fmt.Println()
//fmt.Fprintf(w, “
io.WriteString(w, “ Index Page3”) }
func main() {
http.HandleFunc(”/”, ServeHTTP)
err := http.ListenAndServe(”:9000”, nil)
if err != nil {
log.Fatal(“ERROR: “, err)
}
}
函数func ListenAndServe(addr string, handler Handler) error内部进⾏封装更简洁地实现HTTP服务器。你可以看到它创建了Server类的⼀个对象,然后调⽤了刚才说的第⼀个函数。第2个参数是⼀个Hander,它是⼀个接⼝。这个接⼝很简单,只要某个struct有ServeHTTP(http.ResponseWriter, *http.Request)这个⽅法,那这个struct就⾃动实现了Hander接⼝。
ServeHTTP⽅法,他需要2个参数,⼀个是http.ResponseWriter,另⼀个是http.Request往http.ResponseWriter写⼊什么内容,浏览器的⽹⻚源码就是什么内容。http.Request⾥⾯是封装了,浏览器发过来的请求(包含路径、浏览器类型等等)。
OK,作为服务器我们会处理很多的请求,那下⼀步如何处理呢,难道switch r.URL.Path的值吗?那得多⾟苦。那有什么好的办法呢,这点go官⽅已经考虑到这点帮我们提供了⼀个⽅法叫做ServeMux,去分发任务。
下⾯介绍下ServeMux
ServeMux⼤致作⽤是,他有⼀张map表,map⾥的key记录的是r.URL.String(), ⽽value记录的是⼀个⽅法,这个⽅法和ServeHTTP是⼀样的,这样ServeMux是实现Handler接⼝的。这个⽅法有⼀个别名,叫HandlerFunc。
func main() {
mux := http.NewServeMux()
mux.HandleFunc(”/h”, func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, “hello”)
})
mux.HandleFunc(”/bye”, func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, “byebye”)
})
mux.HandleFunc(“/baidu”, func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, “http://www.baidu.com”,
http.StatusTemporaryRedirect)
})
mux.HandleFunc(“/hello”, sayhello)
http.ListenAndServe(“:9000”, mux) }
func sayhello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, “hello world”) }
回到开头,有让⼤家先忘掉http.HandleFunc(“/”, HandleIndex) 当http.ListenAndServe(“:9000”, nil)的第2个参数是nil时,http内部会⾃⼰建⽴⼀个叫DefaultServeMux的ServeMux,因为这个ServeMux是http⾃⼰维护的,如果要向这个ServeMux注册的话,就要⽤http.HandleFunc这个⽅法啦,现在看很简单吧。
func FileServer
func FileServer(root FileSystem) Handler
FileServer返回⼀个使⽤FileSystem接⼝root提供⽂件访问服务的HTTP处理器。
要使⽤操作系统的FileSystem接⼝实现,可使⽤http.Dir:
log.Fatal(http.ListenAndServe(“:8080”,
http.FileServer(http.Dir(“/usr/share/doc”)))) 服务端获取客户端请求数据
- 获取GET参数
⽐较常⻅的是如下⽅式获取:
r.ParseForm()
if len(r.Form[“id”]) > 0 {
fmt.Fprintln(w, r.Form[“id”][0])
}
其中r表示*http.Request类型,w表示http.ResponseWriter类型。
r.Form是url.Values字典类型,r.Form[“id”]取到的是⼀个数组类型。因为http.request在解析参数的时候会将同名的参数都放进同⼀个数组⾥,所以这⾥要⽤[0]获取到第⼀个。 - 获取POST参数
这⾥要分两种情况:
普通的post表单请求,Content-Type=application/x-www-form-urlencoded 有⽂件上传的表单,Content-Type=multipart/form-data
第⼀种情况⽐较简单,直接⽤PostFormValue就可以取到了。
fmt.Fprintln(w, r.PostFormValue(“id”))
第⼆种⽂件上传的表单这⾥不作讲解。 - 获取COOKIE参数
cookie, err := r.Cookie(“id”)
if err == nil {
fmt.Fprintln(w, “Domain:”, cookie.Domain)
fmt.Fprintln(w, “Expires:”, cookie.Expires)
fmt.Fprintln(w, “Name:”, cookie.Name)
fmt.Fprintln(w, “Value:”, cookie.Value) }
r.Cookie返回*http.Cookie类型,可以获取到domain、过期时间、值等数据。
注意
application/x-www-form-urlencoded与 text/html 的区别
若需使⽤r.PostForm、r.Form等⽅法,必须先调⽤r.ParseForm()
客户端使⽤post提交数据到body时,若服务端需使⽤r.PostForm、r.Form等⽅法,客户端的请求体必须使⽤"Content-Type":“application/x-www-formurlencoded”) 类型,并且r.ParseForm()须放在ioutil.ReadAll(r.Body)解析body之前。否则数据将在body中。



