原文
HTTP
客户库,受python-requests
的启发,目标是:内存占用小,性能,简单,高级的API
,原生D实现
接口文档
配置库
此库不使用vibe-d
,但可与网络IO
的vibe-d
套接字配合使用,因此可在vibe-d
应用中使用请求.要构建vibe-d
变体,请求版本2.x
使用
"dependencies": {
"requests:vibed": "~>2"
}
对请求1.x
,请使用subConfiguration
:
"dependencies": {
"requests": "~>1"
},
"subConfigurations": {
"requests": "vibed"
}
两级API
在最高API
级,只对取或提交
文档内容感兴趣.如果对结果代码
或请求和/或响应
细节不感兴趣,则在不需要加头,设置超时或更改其他默认值
时使用它.
此级建议调用:getContent,postContent,putContent
和patchContent
.你收到的是可作区间
的Buffer
,但可用.data
属性轻松转换为ubyte[]
.
这些调用也有ByLine
对应项,它们懒接收
服务器的响应,按\n
拆分,并转换为ubyte[]
的InputRange
.
//类似
getContentByLine("https://httpbin.org/stream/50").map!"cast(string)a".filter!(a => a.canFind("\"id\": 28"))
//应该可工作
在下级,有封装了http(s)/ftp
传输期望的细节和设置
的请求结构.在请求实例
上操作,可更改与http/ftp
服务器交互的许多配置
.
最重要的API
调用是Request.get(),Reuest.post
或Request.exec!
方法等.你会收到所有可用细节
的响应
:文档正文,状态码,标题,时间
等.
窗口ssl
说明
如果请求在窗口
上找不到openssl
库,以下几个步骤可提供帮助:
从slproweb
下载适合窗口
的最新Win32OpenSSL_Light
安装程序二进制文件.
安装它.重要:允许在系统目录
中安装库.步骤
简单请求
使用dlang-requests
发出HTTP/HTTPS/FTP
请求很简单.首先,安装和导入
请求模块:
import requests;
如果只需要网页内容
,可用getContent()
:
auto content = getContent("http://httpbin.org/");
getContent()
会把完整文档
取到缓冲
,并返回此缓冲
给调用者
(见懒加载内容流
).内容
可转换为串
或区间
.
如,如果要计数内容
行数,可直接应用splitter()
并计数:
writeln(content.splitter('\n').count);
计算非空行
:
writeln(content.splitter('\n').filter!"a!=`".count);
缓冲是带长度(length)和随机访问
的ForwardRange
,因此可直接应用多个算法.或,可用data
属性按ubyte[]
提取数据:
ubyte[] data = content.data;
带参请求
dlang-requests
有带参数
发出请求
的简单方法.如,要模拟
查询人员:姓名-人名,年龄-人员年龄
,等等,
可用queryParams()
助手传递
所有参数来取:
auto content = getContent("http://httpbin.org/get", queryParams("name", "any name", "age", 42));
如果检查httpbin
响应,会看到服务器
识别了所有参数:
{
"args": {
"age": "42",
"name": "any name"
},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/get name=any name&age=42"
}
或,可传递
字典:
auto content = getContent("http://httpbin.org/get", ["name": "any name", "age": "42"]);
得到同样的响应.
如果getContent()
失败
getContent()
(和其他API
调用)可能会触发以下异常:
1,无法连接到文档源
时,出现ConnectError
(无法解析名,拒绝连接
等)
2,单个操作(连接,接收,发送
)超时
时出现超时异常
.
3,从底层
调用接收ErrnoException
时出现ErrnoException
.
4,RequestException
(其他).
提交数据
到服务器
使用dlang-requests
提交的简单方法是postContent()
.有几种方法可提交
数据到服务器:
1,用application/x-www-form-urlencoded
提交到Web
表单,提交短数据
.
2,用multipart/form-data
提交到Web
表单,上传大数据和文件
.
3,无表单
时提交数据
到服务器.
urlencode
表单
用与getContent()
相同方式,使用参数
调用postContent()
:
import std.stdio;
import requests;
void main() {
auto content = postContent("http://httpbin.org/post", queryParams("name", "any name", "age", 42));
writeln(content);
}
输出:
{
"args": {
},
"data": "",
"files": {
},
"form": {
"age": "42",
"name": "any name"
},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Content-Length": "22",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
多部分表单
提交多部分
表单需要准备MultipartForm
结构:
import std.stdio;
import std.conv;
import std.string;
import requests;
void main() {
MultipartForm form;
form.add(formData("name", "any name"));
form.add(formData("age", to!string(42)));
form.add(formData("raw data", "some bytes".dup.representation));
auto content = postContent("http://httpbin.org/post", form);
writeln("输出:");
writeln(content);
}
输出:
{
"args": {
},
"data": "",
"files": {
},
"form": {
"age": "42",
"name": "any name",
"raw data": "some bytes"
},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Content-Length": "332",
"Content-Type": "multipart/form-data; boundary=e3beab0d-d240-4ec1-91bb-d47b08af5999",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
以下是提交
文件示例:
import std.stdio;
import std.conv;
import std.string;
import requests;
void main() {
MultipartForm form;
form.add(formData("file", File("test.txt", "rb"), ["filename":"test.txt", "Content-Type": "text/plain"]));
form.add(formData("age", "42"));
auto content = postContent("http://httpbin.org/post", form);
writeln("输出:");
writeln(content);
}
输出:
{
"args": {
},
"data": "",
"files": {
"file": "this is test file\n"
},
"form": {
"age": "42"
},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Content-Length": "282",
"Content-Type": "multipart/form-data; boundary=3fd7317f-7082-4d63-82e2-16cfeaa416b4",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
无表单时提交原始数据
postContent()
可从InputRanges
提交.如,要提交文件内容:
import std.stdio;
import requests;
void main() {
auto f = File("test.txt", "rb");
auto content = postContent("http://httpbin.org/post", f.byChunk(5), "application/binary");
writeln("输出:");
writeln(content);
}
输出:
{
"args": {
},
"data": "this is test file\n",
"files": {
},
"form": {
},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Content-Length": "18",
"Content-Type": "application/binary",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
或,如果在内存
中保存
数据,则可如下:
auto content = postContent("http://httpbin.org/post", "ABCDEFGH", "application/binary");
这些都是用默认请求参数
的简单API
的细节.
下一节介绍通过Request
结构的低级接口
.
请求结构
要配置
请求细节(如超时和其他限制
,活跃状态
,ssl
属性)或响应
细节(代码,头
)时,要使用请求和响应
结构:
Request rq = Request();
Response rs = rq.get("https://httpbin.org/");
assert(rs.code==200);
默认,使用Keep-Alive
请求,因此可重用连接:
import std.stdio;
import requests;
void main()
{
auto rq = Request();
rq.verbosity = 2;
auto rs = rq.get("http://httpbin.org/image/jpeg");
writeln(rs.responseBody.length);
rs = rq.get("http://httpbin.org/image/png");
writeln(rs.responseBody.length);
}
后面的rq.get()
重用了前面与服务器
的连接.当主机,协议或端口
变化时,Request
打开自动重新连接
(因此通过单个Request
实例,发送不同请求
是安全的).
当服务器过早关闭
活跃连接时,它也会恢复
.也可在需要
时关闭keepAlive
:
rq.keepAlive = false;
对默认
外内容,可配置请求结构
的:活跃状态
,重定向处理
,添加/删除头
,设置IO
缓冲大小及响应头和正文的最大大小
.
如,要用基本认证
来授权,如下(适合HTTP
和FTPURL
):
rq = Request();
rq.authenticator = new BasicAuthentication("user", "passwd");
rs = rq.get("http://httpbin.org/basic-auth/user/passwd");
以下是可设置的一些请求选项
的简短说明:
名字 |
类型 |
意思 |
默认 |
---|---|---|---|
keepAlive |
bool |
保持活跃 |
true |
maxRedirects*) |
uint |
最大重定向深度(0,禁止) |
10 |
maxHeadersLength*) |
size_t |
可接受的最大响应头长度 |
32KB |
maxContentLength*) |
size_t |
可接受的最大内容长度 |
5MB |
timeout*) |
Duration |
连接或数据传输的持续时间超时 |
30.seconds |
bufferSize |
size_t |
套接字io 缓冲大小 |
16KB |
verbosity |
uint |
详细级(0,1,2or3) |
0 |
proxy |
string |
HTTPproxy网址 |
null |
addHeaders |
string[string] |
附加头 |
null |
useStreaming |
bool |
按懒InputRange接收数据 |
false |
cookie |
Cookie[] |
发送到服务器的cookie |
null |
authenticator |
Auth |
认证器 |
null |
bind |
string |
连接时用本地地址 |
null |
socketFactory |
NetworkStream |
用户提供的连接工厂 |
null |
*)
表示达到限制时触发异常
.
请求只读属性:
名字 |
类型 |
意思 |
---|---|---|
cookie |
Cookie[] |
服务器发送给的cookie |
contentLength |
long |
当前文档内容长度 ,如果未知,则为-1 |
contentReceived |
long |
接收内容 |
重定向和连接优化
在小缓存
中保留
请求的永久重定向
的结果.它还保留(schema,host,port)->已打开连接
的映射,以供后续使用.
流式服务器响应
计划在响应
中接收非常大的内容
(文件下载
)时,不想把千兆字节
内容放到响应
缓冲中.使用useStreaming
,可按输入区间接收服务器的响应
.
区间元素
是(ubyte[]
类型)数据块
.用contentLength
和contentReceived
来监听
进度:
import std.stdio;
import requests;
void main()
{
auto rq = Request();
rq.useStreaming = true;
rq.verbosity = 2;
auto rs = rq.get("http://httpbin.org/image/jpeg");
auto stream = rs.receiveAsRange();
while(!stream.empty) {
writefln("Received %d bytes, total received %d from document length %d", stream.front.length, rs.contentReceived, rs.contentLength);
stream.popFront;
}
}
输出:
> GET /image/jpeg HTTP/1.1
> Connection: Keep-Alive
> User-Agent: dlang-requests
> Accept-Encoding: gzip, deflate
> Host: httpbin.org
>
< HTTP/1.1 200 OK
< server: nginx
< date: Thu, 09 Jun 2016 16:25:57 GMT
< content-type: image/jpeg
< content-length: 35588
< connection: keep-alive
< access-control-allow-origin: *
< access-control-allow-credentials: true
< 1232 bytes of body received
< 1448 bytes of body received
Received 2680 bytes, total received 2680 from document length 35588
...
详细程度>=3
时,你还收到
从服务器接收
的每个数据部分
的转储:
00000 48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D |HTTP/1.1 200 OK.|
00010 0A 53 65 72 76 65 72 3A 20 6E 67 69 6E 78 0D 0A |.Server: nginx..|
00020 44 61 74 65 3A 20 53 75 6E 2C 20 32 36 20 4A 75 |Date: Sun, 26 Ju|
00030 6E 20 32 30 31 36 20 31 36 3A 31 36 3A 30 30 20 |n 2016 16:16:00 |
00040 47 4D 54 0D 0A 43 6F 6E 74 65 6E 74 2D 54 79 70 |GMT..Content-Typ|
00050 65 3A 20 61 70 70 6C 69 63 61 74 69 6F 6E 2F 6A |e: application/j|
00060 73 6F 6E 0D 0A 54 72 61 6E 73 66 65 72 2D 45 6E |son..Transfer-En|
00070 63 6F 64 69 6E 67 3A 20 63 68 75 6E 6B 65 64 0D |coding: chunked.|
00080 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 |.Connection: kee|
...
为了好玩:通过流媒体,你只需两行代码即可在服务器之间转发内容.postContent
接收自动来自源的下个数据部分并发送其到目标:
import requests;
import std.stdio;
void main()
{
auto rq = Request();
rq.useStreaming = true;
auto stream = rq.get("http://httpbin.org/get").receiveAsRange();
auto content = postContent("http://httpbin.org/post", stream);
writeln(content);
}
可在并行
任务中使用dlang-requests
(但不能在线程
间共享相同的Request
结构):
import std.stdio;
import std.parallelism;
import std.algorithm;
import std.string;
import core.atomic;
import requests;
immutable auto urls = [
"http://httpbin.org/stream/10",
"http://httpbin.org/stream/20",
"http://httpbin.org/stream/30",
"http://httpbin.org/stream/40",
"http://httpbin.org/stream/50",
"http://httpbin.org/stream/60",
"http://httpbin.org/stream/70",
];
void main() {
defaultPoolThreads(5);
shared short lines;
foreach(url; parallel(urls)) {
atomicOp!"+="(lines, getContent(url).splitter("\n").count);
}
assert(lines == 287);
}
文件下载示例
注意:对文件使用"wb"
和rawWrite
.
import requests;
import std.stdio;
void main() {
Request rq = Request();
Response rs = rq.get("http://geoserver.readthedocs.io/en/latest/_images/imagemosaiccreate1.png");
File f = File("123.png", "wb"); //打开文件时记得同时使用`"W"`和`"B"`模式.
f.rawWrite(rs.responseBody.data);
f.close();
}
可能不能把整个文档
加载到内存
中,然后再保存
.此时使用流
:
import requests;
import std.stdio;
void main() {
Request rq = Request();
rq.useStreaming = true;
auto rs = rq.get("http://geoserver.readthedocs.io/en/latest/_images/imagemosaiccreate1.png");
auto stream = rs.receiveAsRange();
File file = File("123.png", "wb");
while(!stream.empty) {
file.rawWrite(stream.front);
stream.popFront;
}
file.close();
}
vibe.d
可安全地将dlang-requests
与vibe.d
一起使用.用支持vibe.d
套接字(--config=vibed)
,编译dlang-requests
时,每次调用dlang-requests
的API
,只能阻止当前纤程
,而不能阻止线程
:
import requests, vibe.d;
shared static this()
{
void taskMain()
{
logInfo("Task created");
auto r1 = getContent("http://httpbin.org/delay/3");
logInfo("Delay request finished");
auto r2 = getContent("http://google.com");
logInfo("Google request finished");
}
setLogFormat(FileLogger.Format.threadTime, FileLogger.Format.threadTime);
for(size_t i = 0; i < 3; i++)
runTask(&taskMain);
}
输出:
[F7EC2FAB:F7ECD7AB2016.07.0516:55:54.115INF]已创建任务
...略...
添加/替换请求头
使用string[string]
和addHeaders()
方法添加或替换
一些请求头
.
用户提供的头
会覆盖
由库代码
创建的头
,因此必须小心添加常见头
,如Content-Type,Content-Length
等.
import requests;
void main() {
auto rq = Request();
rq.verbosity = 2;
rq.addHeaders(["User-Agent": "test-123", "X-Header": "x-value"]);
auto rs = rq.post("http://httpbin.org/post", `{
"a":"b"}`, "application/x-www-form-urlencoded");
}
输出:
> POST /post HTTP/1.1
> Content-Length: 9
> Connection: Keep-Alive
> User-Agent: test-123
> Accept-Encoding: gzip, deflate
> Host: httpbin.org
> X-Header: x-value
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< server: nginx
...
SSL
设置
可为SSL
选项配置HTTP
请求:可启用或禁止
远程服务器的证书验证
,设置授权
远程服务器的密钥和证书
:
1,sslSetVerifyPeer(bool)
,打开或关闭ssl
对端验证(从v0.8.0
开始默认开启
)
2,sslSetKeyFile(string)
,从文件
加载客户密钥
3,sslSetCertFile(string)
,从文件加载客户证书
4,sslSetCaCert(string)
,从私有或自签名服务器
,加载CA
证书
import std.stdio;
import requests;
import std.experimental.logger;
void main() {
globalLogLevel(LogLevel.trace);
auto rq = Request();
rq.sslSetKeyFile("client01.key"); //设置密钥文件
rq.sslSetCertFile("client01.crt"); //设置证书文件
auto rs = rq.get("https://dlang.org/");
writeln(rs.code);
writeln(rs.responseBody);
}
注意,使用vibe.d
时,必须添加
以下调用
rq.sslSetCaCert("/opt/local/etc/openssl/cert.pem");
用CA
证书文件的路径
(位置可能因操作系统
或openssl
打包而异)替换.
默认,打开SSL
对端验证.如果用服务端自签名证书
,会导致问题.
要解决,可把服务端ca.crt
添加到本地
受信存储中,这里
或使用sslSetCaCert
添加,为单个请求
调用
(rq.sslSetCaCert("ca.crt");)
添加.或简单用rq.sslSetVerifyPeer(false)
禁止对端验证
.
FTP
请求
可用相同
结构来发出包括get
和post
的ftp
请求.
如果请求使用ftp
方案,则相关HTTP
的方法不管用.
下面是一例:
import std.stdio;
import requests;
void main() {
auto rq = Request();
rq.verbosity = 3;
rq.authenticator = new BasicAuthentication("login", "password");
auto f = File("test.txt", "rb");
auto rs = rq.post("ftp://example.com/test.txt", f.byChunk(1024));
writeln(rs.code);
rs = rq.get("ftp://@example.com/test.txt");
writeln(rs.code);
}
FTP
提交的第二个参数,可以是可转换为ubyte[]
的任意内容,也可以是元素类型
(如类似ubyte[)
的InputRange
).
如果post
请求中的路径
不存在,它试创建
必需的目录.
与HTTP
一样,可用相同Request
结构调用多个FTP
请求,它重用已创建(及授权
)的连接
.
FTPLIST
命令
可用rq.execute
方法取单个文件属性或目录列表
:
import std.stdio;
import requests;
void main()
{
auto rq = Request();
auto rs = rq.execute("LIST", "ftp://ftp.iij.ad.jp/pub/FreeBSD/");
writeln(rs.responseBody);
}
输出:
-rw-rw-r-- 1 ftp ftp 35 May 12 09:00 TIMESTAMP
...略...
拦截器
拦截器
可修改,记录或缓存
请求.它们可形成附加到Request
结构的链,以便每个请求
都会通过整个链
.
每个拦截器按输入
接收请求,执行必要操作
并把请求传递
给最终提供请求
并返回响应
的处理器.
下面是使用拦截器
的小示例.假定你有个主应用和某些模块
.主要代码:
import std.stdio;
import mymodule;
void main()
{
auto r = mymodule.doSomething();
writeln(r.length);
}
module:
module mymodule;
import requests;
auto doSomething()
{
return getContent("http://google.com");
}
一天,你决定要把每个http
请求记录
到外部服务
.
或把记录日志代码
添加到执行外部http
调用的mymodule
的每个函数
中.这需要大量
工作和代码,甚至不可能.
最好是使用拦截器
.首先,必须创建logger
类:
class LoggerInterceptor : Interceptor {
Response opCall(Request r, RequestHandler next)
{
writefln("Request %s", r);
auto rs = next.handle(r);
writefln("Response %s", rs);
return rs;
}
}
然后,可用此调用
来检测
请求的每个调用
:
import std.stdio;
import requests;
import mymodule;
class LoggerInterceptor : Interceptor {
Response opCall(Request r, RequestHandler next)
{
writefln("Request %s", r);
auto rs = next.handle(r);
writefln("Response %s", rs);
return rs;
}
}
void main()
{
requests.addInterceptor(new LoggerInterceptor());
auto r = mymodule.doSomething();
writeln(r.length);
}
只需要更改
调用addInterceptor()
.
把拦截器
直接附加
到此结构,你可拦截单个Request
结构(而不是整个请求模块
):
Request rq;
rq.addInterceptor(new LoggerInterceptor());
拦截器可在把r请求
传递给下个处理器
前使用Request()
的getters/setter
更改r
请求.如,可用监听器和注入头
添加认证
方法.
可立即
实现某种缓存并返回缓存
的响应.
(从v1.1.2
开始)可用makeAdapter
,更改
拦截器中的POST
数据.例:
import std.stdio;
import std.string;
import std.algorithm;
import requests;
class I : Interceptor
{
Response opCall(Request rq, RequestHandler next)
{
rq.postData = makeAdapter(rq.postData.map!"toUpper(cast(string)a)");
auto rs = next.handle(rq);
return rs;
}
}
void main()
{
auto f = File("text.txt", "rb");
Request rq;
rq.verbosity = 1;
rq.addInterceptor(new I);
auto rs = rq.post("https://httpbin.org/post", f.byChunk(5));
writeln(rs.responseBody);
}
套接字工厂
每次请求
需要新连接
时,如果已配置
,它都会调用工厂
来创建NetworkStream
的实例.这样,你可(在该库
外)实现:各种代理
,unix
套接字连接
等.
响应结构
此结构
提供接收到的响应
的细节.
响应
中最常用的部分是:
1,code
,从服务器接收的HTTP
或FTP
响应码.
2,responseBody
,在未用流式处理
时,包含完整的文档正文
.在流式
传输模式下,你不能用它.
3,responseHeaders
,string[string]
形式的响应头
(不适合FTP
请求)
4,receiveAsRange
,如果在Request
中设置了useStreaming
,则在从服务器接收
数据时,receiveAsRange
提供InputRange
的(ubyte[]
类型)元素.
请求池
要执行大量请求
时,可用请求池
来加速
.
池是一组按作业接收请求并返回结果
的固定
的工作线程
.
每个作业
都可针对URL
,方法,数据
(POST
请求)及其他参数
配置.
pool
为从作业到结果
的并行映射
,它消耗作业的InputRange
,并尽快生成结果InputRange
.
注意,池不保留
结果顺序.如果要关联作业和结果
,则可用"作业"
的不透明字段
.
下面是用法示例:
import std.algorithm;
import std.datetime;
import std.string;
import std.range;
import requests;
void main() {
Job[] jobs = [
Job("http://httpbin.org/get").addHeaders([
"X-Header": "X-Value",
"Y-Header": "Y-Value"
]),
Job("http://httpbin.org/gzip"),
Job("http://httpbin.org/deflate"),
Job("http://httpbin.org/absolute-redirect/3")
.maxRedirects(2),
Job("http://httpbin.org/range/1024"),
Job("http://httpbin.org/post")
.method("POST")//更改默认`GET`为`POST`
.data("test".representation()) //为`POST`附加数据
.opaque("id".representation), //不透明数据的,在结果中收到相同的信息
Job("http://httpbin.org/delay/3")
.timeout(1.seconds), //设置`timeout`为`1.seconds`,此请求触发异常并失败
Job("http://httpbin.org/stream/1024"),
];
auto count = jobs.
pool(6).
filter!(r => r.code==200).
count();
assert(count == jobs.length - 2, "pool test failed");
iota(20)
.map!(n => Job("http://httpbin.org/post")
.data("%d".format(n).representation))
.pool(10)
.each!(r => assert(r.code==200));
}
再举一例,它结合了更多功能
:
import requests;
import std.stdio;
import std.string;
void main() {
Job[] jobs_array = [
Job("http://0.0.0.0:9998/3"),
Job("http://httpbin.org/post").method("POST").data("test".representation()).addHeaders(["a":"b"]),
Job("http://httpbin.org/post", Job.Method.POST, "test".representation()).opaque([1,2,3]),
Job("http://httpbin.org/absolute-redirect/4").maxRedirects(2),
];
auto p = pool(jobs_array, 10);
while(!p.empty) {
auto r = p.front;
p.popFront;
switch(r.flags) {
case Result.OK:
writeln(r.code);
writeln(cast(string)r.data);
writeln(r.opaque);
break;
case Result.EXCEPTION:
writefln("Exception: %s", cast(string)r.data);
break;
default:
continue;
}
writeln("---");
}
}
输出:
2016-12-29T10:22:00.861:streams.d:connect:973 Failed to connect to 0.0.0.0:9998(0.0.0.0:9998): Unable to connect socket: Connection refused
2016-12-29T10:22:00.861:streams.d:connect:973 Failed to connect to 0.0.0.0:9998(0.0.0.0:9998): Unable to connect socket: Connection refused
Exception: Can't connect to 0.0.0.0:9998
---
200
{
"args": {
},
"data": "test",
"files": {
},
"form": {
},
"headers": {
"A": "b",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "4",
"Content-Type": "application/octet-stream",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
[]
---
200
{
"args": {
},
"data": "test",
"files": {
},
"form": {
},
"headers": {
"Accept-Encoding": "gzip, deflate",
"Content-Length": "4",
"Content-Type": "application/octet-stream",
"Host": "httpbin.org",
"User-Agent": "dlang-requests"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
[1, 2, 3]
---
Exception: 2 redirects reached maxRedirects 2.
---
作业方法
名字 |
参数类型 |
描述 |
---|---|---|
method |
"GET"/"POST"串 |
请求方法 |
data |
immutable(ubyte)[] |
提交请求数据 |
timeout |
Duration |
网络 IO超时 |
maxRedirects |
uint |
最大编号重定向数 |
opaque |
immutable(ubyte)[] |
不透明数据 |
addHeaders |
string[string] |
要添加到请求的头 |
结果字段
名字 |
类型 |
描述 |
---|---|---|
flags |
uint |
标志(OK,EXCEPTION) |
code |
ushort |
响应码 |
data |
immutable(ubyte)[] |
响应体 |
opaque |
immutable(ubyte)[] |
作业中的不透明数据 |
池限制
1,目前在vibe.d
下不管用,它使用vibe.d
并行化.
2,它限制
了你的微调
请求(如,只能通过addHeaders()
添加授权,你不能微调SSL
参数等).
3,(因为使用发送/接收
数据交换)Job
和Result
的数据是不变字节数组
.
国际域名
dlang-requests
通过idna
包支持IDNA
.它提供了unicode
域名和punycode
之间的正确转换,但检查
名字是否符合标准
的能力有限.
静态构建
默认,dlang-requests
会动态链接
到openssl
和crypto
库.为了静态链接
到这些库,有staticssl
的dub
配置(仅限linux!
).
一般用来生成"无提交版
"二进制文件,一般在alpine
上使用musl
的libc
完成.