在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
参考:https://studygolang.com/pkgdoc概念解释:
该图来自https://www.sohu.com/a/208720509_99960938
下面的内容来自http://www.runoob.com/http/http-messages.htmlHTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。 一旦建立连接后,数据消息就通过类似Internet邮件所使用的格式[RFC5322]和多用途Internet邮件扩展(MIME)[RFC2045]来传送。
客户端请求消息客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。 举例:GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
HTTP 协议的 8 种请求方法介绍: HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法 HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。 HTTP 协议中共定义了八种请求方法或者叫“动作”来表明对 Request-URI 指定的资源的不同操作方式,具体介绍如下:
虽然 HTTP 的请求方式有 8 种,但是我们在实际应用中常用的也就是 get 和 post,其他请求方式也都可以通过这两种方式间接的来实现。
服务器响应消息HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。 举例 服务端响应: HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
输出结果: Hello World! My payload includes a trailing CRLF.
1>常量 const ( StatusContinue = 100 StatusSwitchingProtocols = 101 StatusOK = 200 StatusCreated = 201 StatusAccepted = 202 StatusNonAuthoritativeInfo = 203 StatusNoContent = 204 StatusResetContent = 205 StatusPartialContent = 206 StatusMultipleChoices = 300 StatusMovedPermanently = 301 StatusFound = 302 StatusSeeOther = 303 StatusNotModified = 304 StatusUseProxy = 305 StatusTemporaryRedirect = 307 StatusBadRequest = 400 StatusUnauthorized = 401 StatusPaymentRequired = 402 StatusForbidden = 403 StatusNotFound = 404 StatusMethodNotAllowed = 405 StatusNotAcceptable = 406 StatusProxyAuthRequired = 407 StatusRequestTimeout = 408 StatusConflict = 409 StatusGone = 410 StatusLengthRequired = 411 StatusPreconditionFailed = 412 StatusRequestEntityTooLarge = 413 StatusRequestURITooLong = 414 StatusUnsupportedMediaType = 415 StatusRequestedRangeNotSatisfiable = 416 StatusExpectationFailed = 417 StatusTeapot = 418 StatusInternalServerError = 500 StatusNotImplemented = 501 StatusBadGateway = 502 StatusServiceUnavailable = 503 StatusGatewayTimeout = 504 StatusHTTPVersionNotSupported = 505 ) HTTP状态码 当你得到一个值的时候,如果你想要知道这个值代表的是什么状态,可以使用: StatusTextfunc StatusText(code int) string StatusText返回HTTP状态码code对应的文本,如220对应"OK"。如果code是未知的状态码,会返回""。 举例: package main import( "fmt" "net/http" ) func main() { sta1 := http.StatusText(307) sta2 := http.StatusText(200) fmt.Println(sta1) //Temporary Redirect fmt.Println(sta2) //OK }
2>变量 var DefaultClient = &Client{}
DefaultClient是用于包函数Get、Head和Post的默认Client。 var DefaultServeMux = NewServeMux()
DefaultServeMux是用于Serve的默认ServeMux。
3> CanonicalHeaderKeyfunc CanonicalHeaderKey(s string) string
CanonicalHeaderKey函数返回头域(表示为Header类型)的键s的规范化格式。规范化过程中让单词首字母和'-'后的第一个字母大写,其余字母小写。例如,"accept-encoding"规范化为"Accept-Encoding"。 举例: package main
import(
"fmt"
"net/http"
)
func main() {
hea1 := http.CanonicalHeaderKey("uid-test")
hea2 := http.CanonicalHeaderKey("accept-encoding")
fmt.Println(hea1) //Uid-Test
fmt.Println(hea2) //Accept-Encoding
}
DetectContentTypefunc DetectContentType(data []byte) string
DetectContentType函数实现了http://mimesniff.spec.whatwg.org/描述的算法,用于确定数据的Content-Type。函数总是返回一个合法的MIME类型;如果它不能确定数据的类型,将返回"application/octet-stream"。它最多检查数据的前512字节。 出处:https://www.cnblogs.com/52fhy/p/5436673.html Http Header里的Content-Type一般有这三种:
网页中form的enctype属性为编码方式,常用有两种:
1)当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串追加到url后面,用?分割,加载这个新的url。 4)当然还有很多其他的类型,可以查看http://www.runoob.com/http/http-content-type.html 因此可以使用DetectContentType来检测传入的[]byte类型的数据是哪种Content-Type,举例说明:\ package main
import(
"fmt"
"net/http"
)
func main() {
cont1 := http.DetectContentType([]byte{}) //text/plain; charset=utf-8
cont2 := http.DetectContentType([]byte{1, 2, 3}) //application/octet-stream
cont3 := http.DetectContentType([]byte(`<HtMl><bOdY>blah blah blah</body></html>`)) //text/html; charset=utf-8
cont4 := http.DetectContentType([]byte("\n<?xml!")) //text/xml; charset=utf-8
cont5 := http.DetectContentType([]byte(`GIF87a`)) //image/gif
cont6 := http.DetectContentType([]byte("MThd\x00\x00\x00\x06\x00\x01")) //audio/midi
fmt.Println(cont1)
fmt.Println(cont2)
fmt.Println(cont3)
fmt.Println(cont4)
fmt.Println(cont5)
fmt.Println(cont6)
}
const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
TimeFormat是当解析或生产HTTP头域中的时间时,用与time.Parse或time.Format函数的时间格式。这种格式类似time.RFC1123但强制采用GMT时区。 ParseTimefunc ParseTime(text string) (t time.Time, err error)
ParseTime用3种格式TimeFormat, time.RFC850和time.ANSIC尝试解析一个时间头的值(如Date: header)。 举例: package main
import(
"fmt"
"net/http"
"time"
)
var parseTimeTests = []struct {
h http.Header
err bool
}{
{http.Header{"Date": {""}}, true},
{http.Header{"Date": {"invalid"}}, true},
{http.Header{"Date": {"1994-11-06T08:49:37Z00:00"}}, true},
{http.Header{"Date": {"Sun, 06 Nov 1994 08:49:37 GMT"}}, false},
{http.Header{"Date": {"Sunday, 06-Nov-94 08:49:37 GMT"}}, false},
{http.Header{"Date": {"Sun Nov 6 08:49:37 1994"}}, false},
}
func main() {
expect := time.Date(1994, 11, 6, 8, 49, 37, 0, time.UTC)
fmt.Println(expect) //1994-11-06 08:49:37 +0000 UTC
for i, test := range parseTimeTests {
d, err := http.ParseTime(test.h.Get("Date"))
fmt.Println(d)
if err != nil {
fmt.Println(i, err)
if !test.err { //test.err为false才进这里
fmt.Errorf("#%d:\n got err: %v", i, err)
}
continue //有错的进入这后继续下一个循环,不往下执行
}
if test.err { //test.err为true,所以该例子中这里不会进入
fmt.Errorf("#%d:\n should err", i)
continue
}
if !expect.Equal(d) { //说明后三个例子的结果和expect是相同的,所以没有报错
fmt.Errorf("#%d:\n got: %v\nwant: %v", i, d, expect)
}
}
}
返回: userdeMBP:go-learning user$ go run test.go
1994-11-06 08:49:37 +0000 UTC
0001-01-01 00:00:00 +0000 UTC //默认返回的空值
0 parsing time "" as "Mon Jan _2 15:04:05 2006": cannot parse "" as "Mon"
0001-01-01 00:00:00 +0000 UTC
1 parsing time "invalid" as "Mon Jan _2 15:04:05 2006": cannot parse "invalid" as "Mon"
0001-01-01 00:00:00 +0000 UTC
2 parsing time "1994-11-06T08:49:37Z00:00" as "Mon Jan _2 15:04:05 2006": cannot parse "1994-11-06T08:49:37Z00:00" as "Mon"
1994-11-06 08:49:37 +0000 UTC
1994-11-06 08:49:37 +0000 GMT
1994-11-06 08:49:37 +0000 UTC
额外补充,time.Date(): Datefunc Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
ParseHTTPVersionfunc ParseHTTPVersion(vers string) (major, minor int, ok bool)
ParseHTTPVersion解析HTTP版本字符串。如"HTTP/1.0"返回(1, 0, true)。 举例:
package main
import(
"fmt"
"net/http"
)
func main() {
m, n, ok := http.ParseHTTPVersion("HTTP/1.0")
fmt.Println(m, n, ok) //1 0 true
4> 1)header-服务端和客户端的数据都有头部 Headertype Header map[string][]string Header代表HTTP头域的键值对。 你可以自定义自己的Header,下面的Header中只有Date字段,你还可以加入其他字段: http.Header{"Date": {"1994-11-06T08:49:37Z00:00"}} 然后就能够调用下面的几种方法来对Header进行修改: Getfunc (h Header) Get(key string) string Get返回键对应的第一个值,如果键不存在会返回""。如要获取该键对应的值切片,请直接用规范格式的键访问map。 Setfunc (h Header) Set(key, value string)
Set添加键值对到h,如键已存在则会用只有新值一个元素的切片取代旧值切片。 Addfunc (h Header) Add(key, value string)
Add添加键值对到h,如键已存在则会将新的值附加到旧值切片后面。 Delfunc (h Header) Del(key string)
Del删除键值对。 举例: package main import( "fmt" "net/http" ) func main() { header := http.Header{"Date": {"1994-11-06T08:49:37Z00:00"}} fmt.Println(header.Get("Date")) //1994-11-06T08:49:37Z00:00 fmt.Println(header.Get("Content-Type")) //因为没有该字段,返回为空 header.Set("Content-Type", "text/plain; charset=UTF-8") //设置"Content-Type"字段 fmt.Println(header.Get("Content-Type")) //返回text/plain; charset=UTF-8 header.Set("Content-Type", "application/x-www-form-urlencoded;") //覆盖原先的值,返回application/x-www-form-urlencoded; fmt.Println(header.Get("Content-Type")) header.Add("Content-Type", "charset=UTF-8") //在"Content-Type"字段中追加值 fmt.Println(header) //map[Date:[1994-11-06T08:49:37Z00:00] Content-Type:[application/x-www-form-urlencoded; charset=UTF-8]],可见添加进去 fmt.Println(header.Get("Content-Type")) //但是这样获取是返回值仍是application/x-www-form-urlencoded; header.Del("Content-Type") //删除该字段 fmt.Println(header.Get("Content-Type")) //然后返回又为空 } Writefunc (h Header) Write(w io.Writer) error Write以有线格式将头域写入w。 WriteSubsetfunc (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error WriteSubset以有线格式将头域写入w。当exclude不为nil时,如果h的键值对的键在exclude中存在且其对应值为真,该键值对就不会被写入w。 举例: package main import( "fmt" "net/http" "bytes" "os" ) var headerWriteTests = []struct { h http.Header exclude map[string]bool expected string }{ {http.Header{}, nil, ""}, { http.Header{ "Content-Type": {"text/html; charset=UTF-8"}, "Content-Length": {"0"}, }, nil, "Content-Length: 0\r\nContent-Type: text/html; charset=UTF-8\r\n", }, { http.Header{ "Expires": {"-1"}, "Content-Length": {"0"}, "Content-Encoding": {"gzip"}, }, map[string]bool{"Content-Length": true}, //"Content-Length"字段将不会写入io.Writer "Content-Encoding: gzip\r\nExpires: -1\r\n", }, } func main() { var buf bytes.Buffer //得到io.Writer for i, test := range headerWriteTests { test.h.WriteSubset(&buf, test.exclude) fmt.Println(i) buf.WriteTo(os.Stdout) fmt.Println() if buf.String() != test.expected { fmt.Errorf("#%d:\n got: %q\nwant: %q", i, buf.String(), test.expected) } buf.Reset() } } 返回: userdeMBP:go-learning user$ go run test.go 0 1 Content-Length: 0 Content-Type: text/html; charset=UTF-8 2 Content-Encoding: gzip Expires: -1
2)Cookie ⚠️session和cookie的区别: session是存储在服务器的文件,cookie内容保存在客户端,存在被客户篡改的情况,session保存在服务端端防止被用户篡改的情况。 1》 Cookietype Cookie struct { Name string Value string Path string Domain string Expires time.Time RawExpires string // MaxAge=0表示未设置Max-Age属性 // MaxAge<0表示立刻删除该cookie,等价于"Max-Age: 0" // MaxAge>0表示存在Max-Age属性,单位是秒 MaxAge int Secure bool HttpOnly bool Raw string Unparsed []string // 未解析的“属性-值”对的原始文本 } Cookie代表一个出现在HTTP回复的头域中Set-Cookie头的值里或者HTTP请求的头域中Cookie头的值里的HTTP cookie。 Stringfunc (c *Cookie) String() string
String返回该cookie的序列化结果。如果只设置了Name和Value字段,序列化结果可用于HTTP请求的Cookie头或者HTTP回复的Set-Cookie头;如果设置了其他字段,序列化结果只能用于HTTP回复的Set-Cookie头。 1)举例: package main import( "fmt" "net/http" "bytes" "os" "log" "time" ) var writeSetCookiesTests = []struct { Cookie *http.Cookie Raw string }{ { &http.Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}, "cookie-2=two; Max-Age=3600", }, { &http.Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, "cookie-3=three; Domain=example.com", }, { &http.Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, "cookie-4=four; Path=/restricted/", }, { &http.Cookie{Name: "cookie-9", Value: "expiring", Expires: time.Unix(1257894000, 0)}, "cookie-9=expiring; Expires=Tue, 10 Nov 2009 23:00:00 GMT", }, // According to IETF 6265 Section 5.1.1.5, the year cannot be less than 1601 {//故意将这里的cookie-10写成cookie-101,然后下面就会报错 &http.Cookie{Name: "cookie-10", Value: "expiring-1601", Expires: time.Date(1601, 1, 1, 1, 1, 1, 1, time.UTC)}, "cookie-101=expiring-1601; Expires=Mon, 01 Jan 1601 01:01:01 GMT", }, { //因此其返回值中没有Expires &http.Cookie{Name: "cookie-11", Value: "invalid-expiry", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)}, "cookie-11=invalid-expiry", }, // The "special" cookies have values containing commas or spaces which // are disallowed by RFC 6265 but are common in the wild. { &http.Cookie{Name: "special-1", Value: "a z"}, `special-1="a z"`, }, { &http.Cookie{Name: "empty-value", Value: ""}, `empty-value=`, }, { nil, ``, }, { &http.Cookie{Name: ""}, ``, }, { &http.Cookie{Name: "\t"}, ``, }, } func main() { defer log.SetOutput(os.Stderr) var logbuf bytes.Buffer log.SetOutput(&logbuf) for i, tt := range writeSetCookiesTests {//没有报错则说明得到的Cookie的值与Raw字符串相等 if g, e := tt.Cookie.String(), tt.Raw; g != e { fmt.Printf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g) continue } } } 返回: userdeMBP:go-learning user$ go run test.go Test 4, expecting: cookie-101=expiring-1601; Expires=Mon, 01 Jan 1601 01:01:01 GMT Got: cookie-10=expiring-1601; Expires=Mon, 01 Jan 1601 01:01:01 GMT 2)SetCookiefunc SetCookie(w ResponseWriter, cookie *Cookie) SetCookie在w的头域中添加Set-Cookie头,该HTTP头的值为cookie。 常用SetCookie来给http的request请求或者http的response响应设置cookie。 然后使用request的Cookies()、Cookie(name string)函数和response的Cookies()函数来获取设置的cookie信息 ResponseWritertype ResponseWriter interface { // Header返回一个Header类型值,该值会被WriteHeader方法发送。 // 在调用WriteHeader或Write方法后再改变该对象是没有意义的。 Header() Header // WriteHeader该方法发送HTTP回复的头域和状态码。 // 如果没有被显式调用,第一次调用Write时会触发隐式调用WriteHeader(http.StatusOK) // WriterHeader的显式调用主要用于发送错误码。 WriteHeader(int) // Write向连接中写入作为HTTP的一部分回复的数据。 // 如果被调用时还未调用WriteHeader,本方法会先调用WriteHeader(http.StatusOK) // 如果Header中没有"Content-Type"键, // 本方法会使用包函数DetectContentType检查数据的前512字节,将返回值作为该键的值。 Write([]byte) (int, error) } ResponseWriter接口被HTTP处理器用于构造HTTP回复。 举例: 全部评论
|
请发表评论