我在Erlang世界中很陌生,我正在尝试为Twitter Stream API编写一个客户端。我正在使用 httpc:request 发出 POST 请求,但我不断收到 401 错误,我发送请求的方式显然做错了......我拥有的看起来像这样:
fetch_data() ->
Method = post,
URL = "https://stream.twitter.com/1.1/statuses/filter.json",
Headers = "Authorization: OAuth oauth_consumer_key="XXX", oauth_nonce="XXX", oauth_signature="XXX%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="XXX", oauth_token="XXX-XXXXX", oauth_version="1.0"",
ContentType = "application/json",
Body = "{"track":"keyword"}",
HTTPOptions = [],
Options = [],
R = httpc:request(Method, {URL, Headers, ContentType, Body}, HTTPOptions, Options),
R.
在这一点上,我相信签名没有问题,因为当尝试使用 curl 访问 API 时,相同的签名工作得很好。我猜我提出请求的方式存在一些问题。
我得到的响应是,按照上面演示的方式提出的请求是:
{ok,{{"HTTP/1.1",401,"Unauthorized"},
[{"cache-control","must-revalidate,no-cache,no-store"},
{"connection","close"},
{"www-authenticate","Basic realm="Firehose""},
{"content-length","1243"},
{"content-type","text/html"}],
"<html>n<head>n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>n<title>Error 401 Unauthorized</title>n</head>n<body>n<h2>HTTP ERROR: 401</h2>n<p>Problem accessing '/1.1/statuses/filter.json'. Reason:n<pre> Unauthorized</pre>n n n n n n n n n n n n n n n n n n n n n</body>n</html>n"}}
尝试使用 curl 时,我正在使用这个:
curl --request 'POST' 'https://stream.twitter.com/1.1/statuses/filter.json' --data 'track=keyword' --header 'Authorization: OAuth oauth_consumer_key="XXX", oauth_nonce="XXX", oauth_signature="XXX%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="XXX", oauth_token="XXX-XXXX", oauth_version="1.0"' --verbose
我把事件搞得很顺利。
任何这方面的帮助将不胜感激,Erlang 是新的,我已经在这个问题上拔头发了很长一段时间。
您的代码存在几个问题:
- 在 Erlang 中,您将参数编码为
JSON 主体,而使用
curl
中,您将参数编码为表单数据 (application/x-www-form-urlencoded
(。Twitter API期待后者。事实上,你会得到一个401,因为OAuth签名不匹配,因为你在计算中包含track=keyword
参数,而Twitter的服务器在没有JSON正文的情况下计算它,因为它应该按照OAuth RFC。您正在使用带有默认选项的
httpc
。这不适用于流式处理 API,因为流式处理永远不会结束。您需要在结果到达时对其进行处理。为此,您需要将{sync, false}
选项传递给httpc
.另请参阅stream
和receiver
选项。
最终,虽然httpc
最初可以访问Twitter流媒体API,但它对你需要围绕它开发的代码几乎没有价值,以便从Twitter API进行流式传输。根据您的需求,您可能希望将其替换为直接构建在ssl
上的简单客户端,特别是考虑到它可以解码HTTP数据包(留给您的是HTTP块编码(。
例如,如果您的关键字很少见,则可能会因httpc
.此外,更新关键字列表或代码可能会更容易,而不会停机而无需httpc
。
直接基于 ssl
的流客户端可以作为gen_server
实现(如果您不遵循 OTP 原则,则可以实现一个简单的过程(,甚至可以更好地实现重新连接策略gen_fsm
。您可以按以下步骤操作:
- 使用
使用
{packet, http_bin}
解码 HTTP 数据包,并且您希望将套接字配置为被动模式{active, false}
。使用
ssl:send/2,3
发送 HTTP 请求数据包(最好作为 iolist,带有二进制文件(。它应该分布在用CRLF(rn
(分隔的几行上,首先是查询行(GET /1.1/statuses/filter.json?... HTTP/1.1
(,然后是包括OAuth标头的标头。确保也包括Host: stream.twitter.com
。以空行结尾。接收 HTTP 响应。您可以使用循环来实现这一点(因为套接字处于被动模式(,调用
ssl:recv/2,3
直到您获得http_eoh
(标头末尾(。通过查看Transfer-Encoding
响应标头,记下服务器是否会向您发送分块数据。使用
ssl:setopts/2
在主动模式下配置套接字,并指定您希望数据包为原始数据包,数据为二进制格式。事实上,如果数据被分块,您可以继续在被动模式下使用套接字。您还可以逐行获取数据或以字符串形式获取数据。这是一个品味问题:raw 是最安全的选择,逐行要求您检查缓冲区大小以防止截断长 JSON 编码的推文。从 Twitter 接收数据作为发送到您的进程的消息,可以使用
receive
(简单流程(或handle_info
处理程序(如果您使用gen_server
实现了这一点(。如果数据被分块,您将首先收到块大小,然后是推文和块的末尾(参见RFC 2616(。准备好让推文分布在几个块上(即维护某种缓冲区(。这里最好的是在此过程中进行最少的解码,并将推文发送到另一个进程,可能采用二进制格式。
ssl:connect/3,4
进行连接,指定您希望套接字您还应该处理错误和 Twitter 关闭的套接字。确保你遵循 Twitter 的重新连接指南。