HTTP 客户端(http)
发送 HTTP / HTTPS 请求。模块名 http 是协议名,没有拼音名;函数有拼音名。
daoru "http"
rukou() {
changshi {
bl r = http.huoqu("https://example.com/")
dayin(r.status_code)
dayin(http.wenben(&r))
} buhuo (e) {
dayin(e.xinxi)
}
}import "http"
rukou() {
try {
var r = http.get("https://example.com/")
print(r.status_code)
print(http.text(&r))
} catch (e) {
print(e.msg)
}
}先安装 libcurl
http 模块基于 libcurl,cpro 不自带,要先装好:
| 系统 | 安装 |
|---|---|
| Ubuntu / Debian | sudo apt install libcurl4-openssl-dev |
| Windows(MSYS2 UCRT64) | pacman -S mingw-w64-ucrt-x86_64-curl |
只有导入了 http 的程序才需要 libcurl。没装时会在链接时报 cannot find -lcurl。
在 Windows 上把程序发给别人时,要把 libcurl 的 DLL 一起带上。
函数一览
| 拼音 | 英文 | 签名 | 说明 |
|---|---|---|---|
huoqu | get | (url zifuchuan, opt 选项 = {}) 响应 kecuo | GET 请求 |
tijiao | post | (url zifuchuan, body [w8], opt 选项 = {}) 响应 kecuo | POST 字节 |
tijiao_wenben | post_string | (url zifuchuan, body zifuchuan, opt 选项 = {}) 响应 kecuo | POST 文本 |
toubu | header | (r &响应, name zifuchuan) kexuan(zifuchuan) | 取第一个同名响应头 |
toubu_quanbu | header_all | (r &响应, name zifuchuan) [zifuchuan] | 取所有同名响应头 |
wenben | text | (r &响应) zifuchuan kecuo | 响应体转成文本 |
请求选项
三个选项都有默认值,传参时写一个不带类型名的结构体字面量,只写要改的字段:
| 字段 | 默认值 | 说明 |
|---|---|---|
headers | [] | 请求头,每个元素一行 "Name: value" |
timeout_ms | 30000 | 整个请求的总时长(毫秒),0 表示不限 |
verify_tls | true | 是否校验 HTTPS 证书 |
daoru "http"
rukou() {
bl body = "{\"name\":\"cpro\"}"
changshi {
bl r = http.tijiao_wenben("https://example.com/api", body, {
headers: ["Content-Type: application/json"],
timeout_ms: 10000,
})
dayin(r.status_code)
} buhuo (e) {
dayin(e.xinxi)
}
}import "http"
rukou() {
var body = "{\"name\":\"cpro\"}"
try {
var r = http.post_string("https://example.com/api", body, {
headers: ["Content-Type: application/json"],
timeout_ms: 10000,
})
print(r.status_code)
} catch (e) {
print(e.msg)
}
}- POST 时请自己设置 Content-Type,不设时默认是
application/x-www-form-urlencoded,发 JSON 很可能被服务端拒绝 - 请求头里不能含换行符,否则报错(防止请求头注入)
verify_tls: false只用于自签名证书的测试环境,不要用在正式代码里timeout_ms是从连接到收完响应的总时长,下载大文件时可能需要调大
响应
响应的三个字段可以直接读,字段名只有英文:
| 字段 | 类型 | 说明 |
|---|---|---|
r.status_code | z64 | 状态码,如 200、404 |
r.headers | [zifuchuan] | 响应头,每个元素一行 "Name: value" |
r.body | [w8] | 响应体的原始字节 |
4xx / 5xx 不是错误。只要服务端返回了响应,请求就算成功,状态码由你自己判断。进入 buhuo 表示请求本身没完成(连不上、超时、证书错误等):
changshi {
bl r = http.huoqu("https://example.com/user/1")
ruguo (r.status_code == 200) {
dayin(http.wenben(&r))
} fouze ruguo (r.status_code == 404) {
dayin("用户不存在")
} fouze {
dayin("服务端返回", r.status_code)
}
} buhuo (e) {
dayin("请求失败:", e.xinxi)
}try {
var r = http.get("https://example.com/user/1")
if (r.status_code == 200) {
print(http.text(&r))
} else if (r.status_code == 404) {
print("用户不存在")
} else {
print("服务端返回", r.status_code)
}
} catch (e) {
print("请求失败:", e.msg)
}查响应头用 toubu,头名不区分大小写:
bl ct = http.toubu(&r, "content-type")
ruguo (ct != kong) {
dayin(jiebao(ct, ""))
}var ct = http.header(&r, "content-type")
if (ct != none) {
print(unwrap(ct, ""))
}Set-Cookie 这类会重复出现的头,用 toubu_quanbu。
wenben 只认 UTF-8,GBK 等编码的页面会报错。图片等二进制内容直接用 r.body,可以用 文件模块 的 xie_zijie 存盘。
和文件句柄不同,响应可以复制、可以放进数组;但它的类型名同样用不了,要交给别的函数处理时,请传需要的字段(如 r.status_code、r.body)。
不会自动做的事
不跟随重定向:遇到 301、302 等,直接返回这个响应。要跳转就自己读 Location 头再请求一次:
hs get_follow(url zifuchuan) [w8] kecuo {
bl r = sp_kecuo http.huoqu(url)
ruguo (r.status_code >= 300 && r.status_code < 400) {
bl loc = http.toubu(&r, "location")
ruguo (loc != kong) {
r = sp_kecuo http.huoqu(jiebao(loc, ""))
}
}
fanhui r.body
}fn get_follow(url string) [u8] fails {
var r = up_fails http.get(url)
if (r.status_code >= 300 && r.status_code < 400) {
var loc = http.header(&r, "location")
if (loc != none) {
r = up_fails http.get(unwrap(loc, ""))
}
}
return r.body
}不管 Cookie:不保存,也不自动发送。需要时从 Set-Cookie 响应头里读出来,下次请求自己放进 headers。
不编码 URL:URL 里的中文、空格请自己按百分号编码好再传。只支持 http:// 和 https://。
错误码
0–10 和 TCP 的含义相同,11 起是 HTTP 特有的:
| 错误码 | 含义 | 判断函数 |
|---|---|---|
| 1 | 连接被拒绝 | shi_jujue / is_refused |
| 2 | 超时 | shi_chaoshi / is_timeout |
| 3 | 主机不可达或域名解析失败 | shi_bu_keda / is_unreachable |
| 4、5 | 连接被重置或对方已关闭 | shi_yi_guanbi / is_closed |
| 9 | 读写出错,或 wenben 遇到非 UTF-8 内容 | — |
| 11 | 请求不合法:URL 错误、协议不支持、请求头含换行 | — |
| 12 | TLS 握手失败 | shi_woshou_shibai / is_tls |
| 13 | 证书校验失败 | shi_zhengshu_cuowu / is_cert |
| 14 | 服务端的响应不符合 HTTP 协议 | — |
请用 http 自己的判断函数,不要用 wangluo 的。
changshi {
bl r = http.huoqu("https://example.com/")
} buhuo (e) {
ruguo (http.shi_chaoshi(e.daima)) {
dayin("请求超时,请稍后再试")
} fouze ruguo (http.shi_zhengshu_cuowu(e.daima)) {
dayin("证书校验失败:", e.xinxi)
} fouze {
dayin(e.xinxi)
}
}try {
var r = http.get("https://example.com/")
} catch (e) {
if (http.is_timeout(e.code)) {
print("请求超时,请稍后再试")
} else if (http.is_cert(e.code)) {
print("证书校验失败:", e.msg)
} else {
print(e.msg)
}
}证书校验失败(13)的常见原因:服务端用的是自签名证书、证书过期或域名不匹配,或者本机缺少 CA 证书(Ubuntu 上安装 ca-certificates)。
暂不支持
- PUT、DELETE、PATCH、HEAD 请求
- 连接复用、并发请求
- 流式下载、上传进度
- 指定代理(会读取
http_proxy/https_proxy环境变量)、指定 CA 证书文件
