10.14. net/url

网络请求在Web开发中是常见的操作,比如如何进行网络请求、构建对服务器端网络资源的访问。这些操作都可以使用内置库net/url和net/http来实现,本节先学习一个非常小的点路由。在Web开发中,包含如下3个步骤:

(1)设置访问方法:GET、POST、PATCH、DELETE。

(2)设置访问路径,即路由的设计。

(3)设置资源的响应形式,RESTful API风格的响应形式一般选择采用JSON数据格式。

路由:URL(统一资源定位符),唯一定位服务器上的资源。下面通过示例来学习URL一般包含哪些部分。

net/url 库用于解析url。

10.14.1. 1.示例-将字符串转换为URL类型

package main

import (
    "fmt"
    "net/url"
)

func UrlUsage() {
    var urlString = "https://golang.org/pkg/net/url?name=xie&age=20"
    urlPath, _ := url.Parse(urlString)
    fmt.Println(fmt.Sprintf("%#v", urlPath))
    // &url.URL{Scheme:"https", Opaque:"", User:(*url.Userinfo)(nil), Host:"golang.org", Path:"/pkg/net/url", RawPath:"", ForceQuery:false, RawQuery:"name=xie&age=20", Fragment:"", RawFragment:""}

    v := urlPath.Query()
    v.Set("name", "Wei")
    urlPath.RawQuery = v.Encode()
    fmt.Println(fmt.Sprintf("%#v", urlPath))
    // &url.URL{Scheme:"https", Opaque:"", User:(*url.Userinfo)(nil), Host:"golang.org", Path:"/pkg/net/url", RawPath:"", ForceQuery:false, RawQuery:"age=20&name=Wei", Fragment:"", RawFragment:""}
}

func UrlUsageParams() {
    rawUrl := "https://golang.org/pkg/net/url?name=xie&age=20"
    urlParsed, _ := url.Parse(rawUrl)
    fmt.Println(urlParsed.Query())      // map[age:[20] name:[xie]]

    v := urlParsed.Query()
    v.Del("name")
    v.Add("school", "shanghai")
    urlParsed.RawQuery = v.Encode()
    fmt.Println(urlParsed)  // https://golang.org/pkg/net/url?age=20&school=shanghai
}

func UrlValues() {
    values := "name=xie&age=20"
    v, _ := url.ParseQuery(values)
    fmt.Println(v)  // map[age:[20] name:[xie]]

    v.Add("school", "shanghai")
    fmt.Println(v)  // map[age:[20] name:[xie] school:[shanghai]]
}

func main() {
    UrlUsage()
    UrlUsageParams()
    UrlValues()
}

URL核心的操作如下:

(1)将字符串转化为URL类型。

(2)对请求参数的操作。后续在net/http库中经常能看到URL操作的影子。

10.14.2. 2.示例1

package main

import (
    "fmt"
    "log"
    "net/url"
)

func main() {
    // 定义url字符串
    s1 := "https://www.abc.123.com/hello/abc/index.html?k1=world&k2=abc"
    s2 := "postgres://username:password@host.com:5432/path?k=v#f"

    // 解析url
    u1, err := url.Parse(s1)
    if err != nil {
        log.Printf("解析失败, err: %v\n", err)
        return
    }

    // 解析url
    u2, err := url.Parse(s2)
    if err != nil {
        log.Fatalf("解析失败, err: %v\n", err)
    }

    // 打印解析结构
    fmt.Println(u1)
    fmt.Println(u2)

    // 打印解析结构的类型
    fmt.Printf("u1 type: %T\n", u1)  // *url.URL
    fmt.Printf("u2 type: %T\n", u2)  // *url.URL

    fmt.Println(u1.Scheme)
    fmt.Println(u2.Scheme)

    fmt.Println(u1.User)
    fmt.Println(u2.User)
}

10.14.3. 3.示例2

redirect := "http://cm-wzry.cmcm.com/spread/jump?uri=http%3a%2f%2fa.app.qq.com%2fo%2fsimple.jsp%3fpkgname%3dcom.cmcm.gamemaster%26f%3d6"

// 编码
encodeurl := url.QueryEscape(redirect)
fmt.Println(encodeurl)

// 解码
decodeurl, err := url.QueryUnescape(encodeurl)
if err != nil {
    panic(err)
}
fmt.Println(decodeurl)

url.Parse() 函数返回一个url.URL结构体和一个错误,url.URL源码如下:

type URL struct {
    Scheme      string
    Opaque      string    // encoded opaque data
    User        *Userinfo // username and password information
    Host        string    // host or host:port
    Path        string    // path (relative paths may omit leading slash)
    RawPath     string    // encoded path hint (see EscapedPath method)
    ForceQuery  bool      // append a query ('?') even if RawQuery is empty
    RawQuery    string    // encoded query values, without '?'
    Fragment    string    // fragment for references, without '#'
    RawFragment string    // encoded fragment hint (see EscapedFragment method)
}

// User 字段包含了认证信息,*Userinfo内容如下:
type Userinfo struct {
    username    string
    password    string
    passwordSet bool
}

// UserInfo 结构体有三个方法:
// 返回 username
func (u *Userinfo) Username() string

// 返回 password 和 passwordSet
func (u *Userinfo) Password() (string, bool)

// String returns the encoded userinfo information in the standard form
// of "username[:password]".
func (u *Userinfo) String() string {
    if u == nil {
        return ""
    }
    s := escape(u.username, encodeUserPassword)
    if u.passwordSet {
        s += ":" + escape(u.password, encodeUserPassword)
    }
    return s
}

10.14.4. 4.小结

URL用于唯一定位服务器上的资源,浏览网页实质上就是使用浏览器访问各种服务器上不同的资源。

浏览器渲染出服务器上能够让用户看到的资源,不同的请求参数能够访问到的资源不同,URL通过服务器地址、路径、请求参数等构造出访问不同服务器资源的形式。