本文是一篇学习笔记,记录了作者学习go语言标准库net/url库的过程。参考:https://studygolang.com/pkgdoc导入方法:import"net/url"url包解析url并实现查询的转义码,参见RFC3986funcPathEscapefuncPathEscape(sstring)stringPathEscape转义一个字符串所以它可以安全地放置在URL路径段中。funcPathUnescapefuncPathUnescape(sstring)(string,errorPathUnescape执行PathEscape的逆运算,将%AB转换为字节0xAB。如果任何%后没有两个十六进制数字,它将返回错误。PathUnescape与QueryUnescape相同,只是Willnot将'+'改为''(空格)。funcQueryEscapefuncQueryEscape(sstring)stringQueryEscape函数对s进行转码,使其可以安全地用于URL查询。funcQueryUnescapefuncQueryUnescape(sstring)(string,error)QueryUnescape函数使用用于恢复转码后的字符串通过QueryEscape。它会将%AB更改为字节0xAB,并将'+'更改为''。如果%后面没有跟两个十六进制数字,则此函数将返回错误。例子:packagemainimport("fmt""encoding/base64""net/url""crypto/rand""io""log")//sessionId函数用来生成一个sessionID,它是session的唯一标识funcsessionId()string{b:=make([]byte,32)//ReadFull从rand.Reader中准确读取len(b)字节的数据并填充到b//rand.Reader是一个强随机数生成器,用于全局和共享passwordsif_,err:=io.ReadFull(rand.Reader,b);err!=nil{return""}fmt.Println(b)//[23824623516648196157143123140241200213113247168219132208163223247216211430175205176117139118]returnbase64.URLEncoding.EncodeToString(b)//将生成的随机对数字b进行编码返回字符串,取值为sessionID}funcmain(){sessionId:=sessionId()fmt.Println(sessionId)//7vbrpjDEnY97jPHI1XH3qNuE0KPfGEiich6vzbB1i3Y=encodedSessionId:=url.QueryEscape(sessionId)//转码sessionId使其在URL查询中安全使用fmt.Println(encodedSessionId)//7vbrpjDEnY97jPHI1XH3qNuE0KPfGEiich6vzbB1i3Y%3DdecodedSessionId,err:=url.QueryUnescape(encodedSessionId)//恢复QueryEscape转码后的字符串iferr(!=nil{log.Faerr)}fmt.Println(decodedSessionId)//7vbrpjDEnY97jPHI1XH3qNuE0KPfGEiich6vzbB1i3Y=}typeURLtypeURLstruct{Schemestring//具体是指使用哪种协议来访问服务器上的资源Opaquest//编码的不透明数据User*Userinfo//用户名和密码信息,有些协议需要传入明文用户名和获取资源的密码,如FTPHoststring//host或host:port,服务器地址,可以是IP地址,也可以是域名信息Pathstring//路径,使用“/”分隔RawQuerystring//编码后的查询字符串,不带'?'Fragmentstring//Referencedfragment(documentlocation),without'#'}URL类型表示解析的URL(或者,URL引用)。基本的URL格式如下:scheme://[userinfo@]host/path[?query][#fragment]scheme后不跟冒号和双斜杠的URL被解释为如下格式:scheme:opaque[?query][#fragment]注意path字段是用解码后的格式保存的,比如/%47%6f%2f会变成/Go/。这使我们无法确定Path字段中的斜杠是来自原始URL还是未解码的%2f。这种区别并不重要,除非客户端必须使用其他程序/函数来解析原始URL或重建原始URL。此时HTTP服务器可以查询req.RequestURI,HTTP客户端可以使用URL{Host:"example.com",Opaque:"//example.com/Go%2f"}代替{Host:"example.com”,路径:“/Go/”}。funcParsefuncParse(rawurlstring)(url*URL,errerror)Parse函数将rawurl解析成URL结构,rawurl可以是绝对地址也可以是相对地址。funcParseRequestURIfuncParseRequestURI(rawurlstring)(url*URL,errerror)ParseRequestURI函数将rawurl解析成一个URL结构,这个函数会假设rawurl在一个HTTP请求中,所以它会假设参数是一个绝对URL或者绝对路径,并将假设URL没有#fragment后缀。(Web浏览器将去掉后缀后的URL发送给Web服务器)func(*URL)IsAbsfunc(u*URL)IsAbs()bool函数只有当URL是绝对URL时才返回true。示例:packagemainimport("fmt""net/url")funcmain(){u:=url.URL{Host:"example.com",Path:"foo"}fmt.Println(u.IsAbs())//falseu.Scheme="http"fmt.Println(u.IsAbs())//true}func(*URL)Queryfunc(u*URL)Query()ValuesQuery方法解析RawQuery字段,返回Values类型key-它代表的值对。示例:packagemainimport("fmt""net/url")funcmain(){u:=&url.URL{Scheme:"https",User:url.UserPassword("me","pass"),Host:"示例。com",Path:"foo/bar",RawQuery:"x=1&y=2",Fragment:"anchor",}fmt.Println(u.Query())//map[x:[1]y:[2]]}func(*URL)RequestURIfunc(u*URL)RequestURI()stringRequestURI方法返回编码路径?查询或不透明?查询字符串,用于HTTP请求。packagemainimport("fmt""net/url""log")funcmain(){u,err:=url.Parse("https://example.org/path?foo=bar")iferr!=nil{log.Fatal(err)}fmt.Println(u.RequestURI())///path?foo=bar}func(*URL)Stringfunc(u*URL)String()stringString将URL重构为有效的URL字符串。String将URL重组为有效的URL字符串。结果的一般形式是以下之一:scheme:opaque?query#fragmentsscheme://userinfo@host/path?query#fragment如果u.Opaque不为空,String使用第一种形式;否则它使用第二种形式。要获取路径,String使用u.EscapedPath()。在第二种形式中,以下规则适用:-ifu.Schemeisempty,scheme:isomitted.-ifu.Userisnil,userinfo@isomitted.-ifu.Hostisempty,host/isomitted.-ifu.Schemandu.Hostareemptyandu.Userisnil,theentirescheme://userinfo@host/isomted.-ifu.Hostisnon-emptyandu.Pathbeginswitha/,theformhost/pathdoesnotadditsown/.-ifu.RawQueryisempty,?queryisomted.-ifu.Fragmentisempty,#fragmentisempty。示例:packagemainimport("fmt""net/url")funcmain(){u:=&url.URL{Scheme:"https",User:url.UserPassword("me","pass"),Host:"示例。com",Path:"foo/bar",RawQuery:"x=1&y=2",Fragment:"anchor",}//这是第一种形式fmt.Println(u.String())//https:///me:pass@example.com/foo/bar?x=1&y=2#anchoru.Opaque="opaque"//这是第二种形式fmt.Println(u.String())//https:opaque?x=1&y=2#anchor}func(*URL)EscapedPathfunc(u*URL)EscapedPath()stringEscapedPath返回u.Path的转义形式。通常,任何路径都有多种可能的转义形式。当u.Path被有效转义时,EscapedPath返回u.RawPath。否则,EscapedPath会忽略u.RawPath并自行计算转义形式。String和RequestURI方法使用EscapedPath来构造它们的结果。一般来说,代码应该调用EscapedPath而不是直接读取u.RawPath。示例:packagemainimport("fmt""net/url""log")funcmain(){u,err:=url.Parse("http://example.com/pathwithspaces")iferr!=nil{log.Fatal(err)}fmt.Println(u.EscapedPath())///path%20with%20spaces}func(*URL)Hostnamefunc(u*URL)Hostname()string主机名返回没有任何端口号的u.Host。如果host是带有端口号的IPv6文字,hostname将返回不带方括号的IPv6文字。IPv6文字可能包含区域标识符。示例:packagemainimport("fmt""net/url""log")funcmain(){u,err:=url.Parse("https://example.org:8000/path")//IPV4iferr!=nil{log.Fatal(err)}fmt.Println(u.Hostname())//example.orgu,err=url.Parse("https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:17000")//IPV6iferr!=nil{log.Fatal(err)}fmt.Println(u.Hostname())//2001:0db8:85a3:0000:0000:8a2e:0370:7334}func(*URL)Portfunc(u*URL)Port()stringPort返回不带前导冒号的u.Host的端口部分。如果u.Host不包含端口,则端口返回空字符串。func(*URL)Parsefunc(u*URL)Parse(refstring)(*URL,error)Parse方法使用u作为上下文来解析URL,ref可以是绝对或相对URL。该方法返回nil,解析失败则返回err;否则,返回结果与ResolveReference一致。示例:packagemainimport("fmt""net/url""log")funcmain(){base,err:=url.Parse("http://example.com/directory/")iferr!=nil{log.Fatal(err)}fmt.Println(base)result,err:=base.Parse("./search?q=dotnet")iferr!=nil{log.Fatal(err)}fmt.Println(result)}返回:bogon:~user$goruntestGo.gohttp://example.com/directory/http://example.com/directory/search?q=dotnetfunc(*URL)ResolveReferencefunc(u*URL)ResolveReference(ref*URL)*URL该方法根据绝对URI将URI补全为绝对URI,参见RFC3986section5.2。参数ref可以是绝对URI或相对URI。ResolveReference始终返回一个新的URL实例,即使该实例与u或ref完全相同。如果ref是绝对URI,此方法将忽略引用URI并返回ref的副本。例:ref为相对路径时,会获取其相对于u的绝对路径:packagemainimport("fmt""net/url""log")funcmain(){//相对路径表示u,err:=url。parse("../../..//search?q=dotnet")//相对路径的不同会影响返回结果iferr!=nil{log.Fatal(err)}fmt.Println(u)base,err:=url.Parse("http://example.com/directory/")iferr!=nil{log.Fatal(err)}fmt.Println(base)fmt.Println(base.ResolveReference(u))}返回:bogon:~user$goruntestGo.go../../..//search?q=dotnethttp://example.com/directory/http://example.com/search?q=dotnet如果相对路径是../..//search?q=dotnet,返回结果是一样的,即http://example.com/search?q=dotnet但如果相对路径是..//search?q=dotnet,会返回http://example.com//search?q=dotnet,这不是我们想要的。如果需要相对于目录目录,则写入相对路径。/search?q=dotnetfunc(u*URL)MarshalBinary()(text[]byte,errerror)func(u*URL)MarshalBinary()(text[]byte,errerror)示例:packagemainimport("fmt""net/url""log""reflect")funcmain(){u,_:=url.Parse("https://example.org")b,err:=u.MarshalBinary()//转换成二进制iferr!=nil{log.Fatal(err)}fmt.Println(reflect.TypeOf(b))//[]uint8fmt.Println(b)//[1041161161121155847471011209710911210810146111114103]fmt.Printf("b)s\n",//https://example.org}func(*URL)UnmarshalBinaryfunc(u*URL)UnmarshalBinary(text[]byte)error示例:packagemainimport("fmt""net/url""log""reflect")funcmain(){u:=&url.URL{}//将其从二进制转换为url.URL类型err:=u.UnmarshalBinary([]byte("https://example.org:8000/foo"))iferr!=nil{log.Fatal(err)}fmt.Println(reflect.TypeOf(u))//*url.URLfmt.Println(u)//https://example.org:8000/foofmt.Println(u.Hostname())//example.orgfmt.Println(u.Port())//8000}typeUserinfotype使用rinfostruct{//containshiddenornon-exportedfields}Userinfo类型是对URL的用户名和密码详细信息的不可修改封装必须保证真正的Userinfo值具有用户名(但根据RFC2396可以是空字符串)和一个可选的密码。funcUserfuncUser(usernamestring)*UserinfoUser函数返回一个*Userinfo,用户名设置为用户名,没有设置密码。funcUserPasswordfuncUserPassword(username,passwordstring)*UserinfoUserPassword函数返回一个*Userinfo,用户名设置为username,密码设置为password。此函数应仅在遗留站点上使用,因为它具有很高的风险且不推荐使用,请参阅RFC2396。func(*Userinfo)Usernamefunc(u*Userinfo)Username()stringUsername方法返回用户名。func(*Userinfo)Passwordfunc(u*Userinfo)Password()(string,bool)返回密码,如果设置了密码则返回true,否则返回false。func(*Userinfo)Stringfunc(u*Userinfo)String()stringString方法返回编码后的用户信息,格式为“username[:password]”。示例:packagemainimport("fmt""net/url""log")funcmain(){u:=&url.URL{Scheme:"https",User:url.UserPassword("me","pass"),Host:"example.com",Path:"foo/bar",RawQuery:"x=1&y=2",Fragment:"anchor",}fmt.Println(u.User.Username())//mepassword,b:=u.User.Password()ifb==false{log.Fatal("cannotgetpassword")}fmt.Println(password)//passfmt.Println(u.User.String())//me:pass}typeValuestypeValuesmap[string][]stringValues将映射到一个值列表。一般用于查询参数和表单属性。与字典类型http.Header不同,Values的键是区分大小写的。funcParseQueryfuncParseQuery(querystring)(mValues,errerror)ParseQuery函数解析一个URL编码的查询字符串,返回一个可以表示查询的Values类型的字典。该函数总是返回一个包含所有有效查询参数的非nil字典,err用于描述解码时遇到的第一个错误(如果有)。示例:packagemainimport("fmt""net/url""log")funcmain(){v,err:=url.ParseQuery("friend=Jess&friend=Sarah&fruit=apple&name=Ava")iferr!=nil{log.Fatal(err)}fmt.Println(v)//map[friend:[JessSarah]fruit:[apple]name:[Ava]]}func(Values)Getfunc(vValues)Get(keystring)stringGet会得到对应的value集合键的第一个值。如果没有为键设置值,则返回一个空字符串。要获取值集,请直接使用map。func(Values)Setfunc(vValues)Set(key,valuestring)Set方法将key对应的值集设置为唯一值,并会替换已有的值集。func(Values)A??ddfunc(vValues)Add(key,valuestring)Add将value添加到key关联的值集中的原始值的后面。func(Values)Delfunc(vValues)Del(keystring)Del删除与key关联的值集。func(Values)Encodefunc(vValues)Encode()stringEncode方法将v编码为按键排序的url编码格式(“bar=baz&foo=quux”)。举例:packagemainimport("fmt""net/url")funcmain(){v:=url.Values{}v.Set("name","Ava")v.Add("friend","Jess")v.Add("friend","Sarah")v.Add("fruit","apple")fmt.Println(v.Get("name"))fmt.Println(v.Get("friend"))fmt.Println(v["friend"])fmt.Println(v.Encode())v.Del("name")fmt.Println(v.Encode())}返回:bogon:~user$goruntestGo.goAvaJess[JessSarah]friend=Jess&friend=Sarah&fruit=apple&name=Avafriend=Jess&friend=Sarah&fruit=apple
