2312d,d语言的requests文档

原文
HTTP客户库,受python-requests的启发,目标是:内存占用小,性能,简单,高级的API,原生D实现
接口文档

配置库

此库不使用vibe-d,但可与网络IOvibe-d套接字配合使用,因此可在vibe-d应用中使用请求.要构建vibe-d变体,请求版本2.x使用

"dependencies": {
    
    
  "requests:vibed": "~>2"
}

对请求1.x,请使用subConfiguration:

"dependencies": {
    
    
  "requests": "~>1"
},
"subConfigurations": {
    
    
  "requests": "vibed"
}

两级API

在最高API级,只对取或提交文档内容感兴趣.如果对结果代码请求和/或响应细节不感兴趣,则在不需要加头,设置超时或更改其他默认值时使用它.

此级建议调用:getContent,postContent,putContentpatchContent.你收到的是可作区间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.postRequest.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缓冲大小及响应头和正文的最大大小.

如,要用基本认证来授权,如下(适合HTTPFTPURL):

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[]类型)数据块.用contentLengthcontentReceived监听进度:

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-requestsvibe.d一起使用.用支持vibe.d套接字(--config=vibed),编译dlang-requests时,每次调用dlang-requestsAPI,只能阻止当前纤程,而不能阻止线程:

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请求

可用相同结构来发出包括getpostftp请求.
如果请求使用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,从服务器接收的HTTPFTP响应码.
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,(因为使用发送/接收数据交换)JobResult的数据是不变字节数组.

国际域名

dlang-requests通过idna包支持IDNA.它提供了unicode域名和punycode之间的正确转换,但检查名字是否符合标准的能力有限.

静态构建

默认,dlang-requests动态链接opensslcrypto库.为了静态链接到这些库,有staticssldub配置(仅限linux!).

一般用来生成"无提交版"二进制文件,一般在alpine上使用musllibc完成.

猜你喜欢

转载自blog.csdn.net/fqbqrr/article/details/135222329
d
<d>