Go语言HTTP Post请求并使用Cookies

46

我想使用 Go 语言登录一个网站并存储 cookie,以便日后使用。

你能提供一个示例代码用于提交表单、存储 cookie 并使用该 cookie 访问另一个页面吗?

我认为我需要创建一个 Client 来存储 cookie,可以参考 http://gotour.golang.org/src/pkg/net/http/client.go 示例。

package main

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

func Login(user, password string) string {
        postUrl := "http://www.pge.com/eum/login"

        // Set up Login
        values := make(url.Values)
        values.Set("user", user)
        values.Set("password", password)

        // Submit form
        resp, err := http.PostForm(postUrl, values)
        if err != nil {
                log.Fatal(err)
        }
        defer resp.Body.Close()

        // How do I store cookies?
        return "Hello"
}

func ViewBill(url string, cookies) string {

//What do I put here?

}

1
不幸的是,标准的cookie Jar实现没有被纳入Go1,但看起来计划在未来进行添加:https://codereview.appspot.com/5544082/ - Nick Craig-Wood
请查看使用http://www.gorillatoolkit.org/pkg/sessions。 - elithrar
5个回答

90

Go 1.1 引入了一个 cookie jar 实现 net/http/cookiejar

import (
    "net/http"
    "net/http/cookiejar"
)

jar, err := cookiejar.New(nil)
if err != nil { 
  // error handling 
}

client := &http.Client{
    Jar: jar,
}

我使用这个代码得到了 undefined: cookieJar。 - Salvatore Timpani
@SalvatoreTimpani 你忘了导入(import)。加上导入(import)它就能正常运行。 - chmike

24

首先,您需要实现 http.CookieJar 接口。然后将其传递给您创建的客户端,它将用于使用该客户端发出的请求。以下是一个基本示例:

package main

import (
    "fmt"
    "net/http"
    "net/url"
    "io/ioutil"
    "sync"
)

type Jar struct {
    lk      sync.Mutex
    cookies map[string][]*http.Cookie
}

func NewJar() *Jar {
    jar := new(Jar)
    jar.cookies = make(map[string][]*http.Cookie)
    return jar
}

// SetCookies handles the receipt of the cookies in a reply for the
// given URL.  It may or may not choose to save the cookies, depending
// on the jar's policy and implementation.
func (jar *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
    jar.lk.Lock()
    jar.cookies[u.Host] = cookies
    jar.lk.Unlock()
}

// Cookies returns the cookies to send in a request for the given URL.
// It is up to the implementation to honor the standard cookie use
// restrictions such as in RFC 6265.
func (jar *Jar) Cookies(u *url.URL) []*http.Cookie {
    return jar.cookies[u.Host]
}

func main() {
    jar := NewJar()
    client := http.Client{nil, nil, jar}

    resp, _ := client.PostForm("http://www.somesite.com/login", url.Values{
        "email": {"myemail"},
        "password": {"mypass"},
    })
    resp.Body.Close()

    resp, _ = client.Get("http://www.somesite.com/protected")

    b, _ := ioutil.ReadAll(resp.Body)
    resp.Body.Close()

    fmt.Println(string(b))
}

你应该使用以主机名为键的映射来管理Cookie。 - Kamil Kisiel
1
CookieJar的实现必须对多个goroutine的并发使用是安全的。这不是并发安全的,更不用说这会将您的cookie发送到其他主机了。我要点踩。 - Stephen Weinberg
针对可能会被复制粘贴的内容,以下是一些要点。我在SetCookies上更新了一个锁定/解锁的示例,但对于Cookies并不确定。新的切片和cookie副本可以确保线程安全,但我不知道该方法在内部如何使用。既然我无法回答这个问题,那么我就自己给自己投下反对票,以免复制粘贴者受到影响! - dskinner
1
除非你在某处设置 jar.cookies = map[string][]*http.Cookie{},否则你将会得到一个 panic: runtime error: assignment to entry in nil map 错误提示 ;-) - matt
对于使用此方法并遇到丢失 cookie 问题的任何人,请查看 https://dev59.com/65bfa4cB1Zd3GeqPyL8Z#37043843 以获取修复方法。 - Twisted1919
显示剩余2条评论

18

在Go语言的1.5版本中,我们可以使用http.NewRequest函数并携带cookie来进行POST请求。

package main                                                                                              
import "fmt"
import "net/http"
import "io/ioutil"
import "strings"

func main() {
    // Declare http client
    client := &http.Client{}

    // Declare post data
    PostData := strings.NewReader("useId=5&age=12")

    // Declare HTTP Method and Url
    req, err := http.NewRequest("POST", "http://localhost/", PostData)

    // Set cookie
    req.Header.Set("Cookie", "name=xxxx; count=x")
    resp, err := client.Do(req)
    // Read response
    data, err := ioutil.ReadAll(resp.Body)

    // error handle
    if err != nil {
        fmt.Printf("error = %s \n", err);
    }   

    // Print response
    fmt.Printf("Response = %s", string(data));
}           

7

net/http/cookiejar 是一个不错的选择,但我想知道在发出请求时实际需要哪些 cookies。您可以通过以下方式获取响应 cookies:

package main
import "net/http"

func main() {
   res, err := http.Get("https://stackoverflow.com")
   if err != nil {
      panic(err)
   }
   for _, c := range res.Cookies() {
      println(c.Name, c.Value)
   }
}

您可以像这样添加 cookies:

package main
import "net/http"

func main() {
   req, err := http.NewRequest("GET", "https://stackoverflow.com", nil)
   if err != nil {
      panic(err)
   }
   req.AddCookie(&http.Cookie{Name: "west", Value: "left"})
}

-3

另一种方法。在Go 1.8中有效。

    expiration := time.Now().Add(5 * time.Minute)
    cookie := http.Cookie{Name: "myCookie", Value: "Hello World", Expires: expiration}
    http.SetCookie(w, &cookie)

5
他要求使用 HTTP 客户端,而不是服务器。 - Ninh Pham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接