如何从 GET 响应中获取 cookie?

How to get the cookie from a GET response?

我正在编写一个向网站发出 GET 请求和 returns 响应 cookie 的函数:

extern crate futures;
extern crate hyper;
extern crate tokio_core;

use tokio_core::reactor::Core;
use hyper::Client;
use std::error::Error;
use hyper::header::Cookie;
use futures::future::Future;

fn get_new_cookie() -> Result<String, Box<Error>> {
    println!("Getting cookie...");
    let core = Core::new()?;
    let client = Client::new(&core.handle());
    println!("Created client");

    let uri = "http://www.cnn.com".parse().expect("Cannot parse url");
    println!("Parsed url");
    let response = client.get(uri).wait().expect("Cannot get url.");
    println!("Got response");
    let cookie = response
        .headers()
        .get::<Cookie>()
        .expect("Cannot get cookie");
    println!("Cookie: {}", cookie);

    Ok(cookie)
}

fn main() {
    println!("{:?}", get_new_cookie());
}

这行不通;它卡在 client.get(...) 字符串上。我得到的输出是:

Getting cookie...
Created client
Parsed url

然后什么也没有发生。

我哪里做错了,我该如何改变它才能正常工作?

参见 documentation for the wait method:

Note: This method is not appropriate to call on event loops or similar I/O situations because it will prevent the event loop from making progress (this blocks the thread). This method should only be called when it's guaranteed that the blocking work associated with this future will be completed by another thread.

Future::wait 已经是 deprecated in the tokio-reform 分支。

我建议设计完整的应用程序来处理异步概念(即 get_new_cookie 应该采用 Handle 和 return 和 Future,而不是分配它的自己的事件循环)。

您可以 运行 使用 Core::run 的请求,如下所示:

let response = core.run(client.get(uri)).expect("Cannot get url.");

因为 , by calling wait, you are putting the thread to sleep until the future has completed. However, that thread needs to run the event loop, so you've just caused a deadlock. Using Core::run 更正确。

因为 , the "Cookie" header is used to send a cookie to the server. SetCookie 用于向客户端 发送 cookie 。它还 returns 所有 cookie 的矢量:

fn get_new_cookie() -> Result<String, Box<Error>> {
    println!("Getting cookie...");
    let mut core = Core::new()?;

    let client = Client::new(&core.handle());
    println!("Created client");

    let uri = "http://www.cnn.com".parse().expect("Cannot parse url");
    println!("Parsed url");

    let response = core.run(client.get(uri)).expect("Cannot get url.");
    println!("Got response");

    let cookie = response
        .headers()
        .get::<SetCookie>()
        .expect("Cannot get cookie");
    println!("Cookie: {:?}", cookie);

    Ok(cookie.join(","))
}

但是,如果您只需要同步 API,请改用 Reqwest。它建立在 Hyper 之上:

extern crate reqwest;

use std::error::Error;
use reqwest::header::SetCookie;

fn get_new_cookie() -> Result<String, Box<Error>> {
    let response = reqwest::get("http://www.cnn.com")?;

    let cookies = match response.headers().get::<SetCookie>() {
        Some(cookies) => cookies.join(","),
        None => String::new(),
    };

    Ok(cookies)
}

fn main() {
    println!("{:?}", get_new_cookie());
}

reqwest 0.11(也许更早)更新

get_new_cookie 函数中,我相信从 reqwest::Response 检索 cookie 的代码片段是这样的:

// returns Option<&HeaderValue>

response.headers().get(http::header::SET_COOKIE)