我一直在使用AWS Kinesis遇到一些问题,因为我设置了一个流,并且我想使用标准的http POST
请求来调用我的流上的Kinesis PutRecord调用。我之所以这样做,是因为我生成的javascript应用程序的包大小很重要,我宁愿不导入aws-sdk
来完成应该(在纸面上)可能的事情。
就在你知道的时候,我已经看过this other stack overflow question about the same thing了,这是......有点信息。
现在,我已经有了一种使用访问密钥,秘密令牌和会话令牌对sigv4签名请求的方法。但是当我最终得到签名请求的结果并使用浏览器中的fetch
api发送它时,服务坦克(或者json对象引用相同的东西,取决于我的Content-Type标题,我猜测)结果。
这是我正在使用的代码
// There is a global function "sign" that does sigv4 signing
// ...
var payload = {
Data: { task: "Get something working in kinesis" },
PartitionKey: "1",
StreamName: "MyKinesisStream"
}
var credentials = {
"accessKeyId": "<access.key>",
"secretAccessKey": "<secret.key>",
"sessionToken": "<session.token>",
"expiration": 1528922673000
}
function signer({ url, method, data }) {
// Wrapping with URL for piecemeal picking of parsed pieces
const parsed = new URL(url);
const [ service, region ] = parsed.host.split(".");
const signed = sign({
method,
service,
region,
url,
// Hardcoded
headers : {
Host : parsed.host,
"Content-Type" : "application/json; charset=UTF-8",
"X-Amz-Target" : "Kinesis_20131202.PutRecord"
},
body : JSON.stringify(data),
}, credentials);
return signed;
}
// Specify method, url, data body
var signed = signer({
method: "POST",
url: "https://kinesis.us-west-2.amazonaws.com",
data : JSON.stringify(payload)
});
var request = fetch(signed.url, signed);
当我查看请求的结果时,我得到了这个:
{
Output: {
__type: "com.amazon.coral.service#InternalFailure"},
Version: "1.0"
}
现在我不确定Kinesis是否真的在这里失败,或者我的输入是否格式不正确?
这是签名请求的样子
{
"method": "POST",
"service": "kinesis",
"region": "us-west-2",
"url": "https://kinesis.us-west-2.amazonaws.com",
"headers": {
"Host": "kinesis.us-west-2.amazonaws.com",
"Content-Type": "application/json; charset=UTF-8",
"X-Amz-Target": "Kinesis_20131202.PutRecord",
"X-Amz-Date": "20180613T203123Z",
"X-Amz-Security-Token": "<session.token>",
"Authorization": "AWS4-HMAC-SHA256 Credential=<access.key>/20180613/us-west-2/kinesis/aws4_request, SignedHeaders=content-type;host;x-amz-target, Signature=ba20abb21763e5c8e913527c95a0c7efba590cf5ff1df3b770d4d9b945a10481"
},
"body": "\"{\\\"Data\\\":{\\\"task\\\":\\\"Get something working in kinesis\\\"},\\\"PartitionKey\\\":\\\"1\\\",\\\"StreamName\\\":\\\"MyKinesisStream\\\"}\"",
"test": {
"canonical": "POST\n/\n\ncontent-type:application/json; charset=UTF-8\nhost:kinesis.us-west-2.amazonaws.com\nx-amz-target:Kinesis_20131202.PutRecord\n\ncontent-type;host;x-amz-target\n508d2454044bffc25250f554c7b4c8f2e0c87c2d194676c8787867662633652a",
"sts": "AWS4-HMAC-SHA256\n20180613T203123Z\n20180613/us-west-2/kinesis/aws4_request\n46a252f4eef52991c4a0903ab63bca86ec1aba09d4275dd8f5eb6fcc8d761211",
"auth": "AWS4-HMAC-SHA256 Credential=<access.key>/20180613/us-west-2/kinesis/aws4_request, SignedHeaders=content-type;host;x-amz-target, Signature=ba20abb21763e5c8e913527c95a0c7efba590cf5ff1df3b770d4d9b945a10481"
}
(测试密钥由生成签名的库使用,因此请忽略它) (还有可能在主体中有额外的斜杠,因为我使用JSON.stringify打印了响应对象)。
我的问题:我有什么遗失的东西吗? Kinesis是否需要标题a,b和c而我只生成两个标题?或者这个内部错误是否真正失败。我输了,因为回答表明我无能为力。
我感谢任何帮助!
编辑:作为第二个问题,我是否正确使用X-Amz-Target
标题?这个是你引用服务功能的方式,只要你点击那个服务端点,不是吗?
更新:关注迈克尔的评论,我已经到了某个地方,但我仍然无法解决问题。这就是我的所作所为:
我确保在我的payload
我只在JSON.stringify
属性上运行Data
。
我还将Content-Type
标题修改为"Content-Type" : "application/x-amz-json-1.1"
,因此我收到了更多有用的错误消息。
现在,我的有效载荷仍大致相同:
var payload = {
Data: JSON.stringify({ task: "Get something working in kinesis" }),
PartitionKey: "1",
StreamName: "MyKinesisStream"
}
我的签名者函数体看起来像这样:
function signer({ url, method, data }) {
// Wrapping with URL for piecemeal picking of parsed pieces
const parsed = new URL(url);
const [ service, region ] = parsed.host.split(".");
const signed = sign({
method,
service,
region,
url,
// Hardcoded
headers : {
Host : parsed.host,
"Content-Type" : "application/json; charset=UTF-8",
"X-Amz-Target" : "Kinesis_20131202.PutRecord"
},
body : data,
}, credentials);
return signed;
}
所以我传入了一个部分序列化的对象(至少数据是),当我发送给服务时,我收到了响应:
{"__type":"SerializationException"}
至少略微有用,因为它告诉我输入技术上不正确。但是,为了纠正这个问题,我做了一些事情:
JSON.stringify
Data
键更改为字符串值以查看是否会通过JSON.stringify
,然后运行btoa
因为我在另一篇文章中读到了对某人有用的内容。 但我仍然遇到同样的错误。我觉得我如此接近。你能发现我可能遗失的任何东西或者我没有尝试过的东西吗?我已经得到了零星的未知操作例外,但我认为现在这个序列化让我感到难过。
编辑2:
事实证明,Kinesis只接受base64
编码的字符串。这可能是aws-sdk
提供的一种精确性,但基本上所需要的只是有效负载中的Data: btoa(JSON.stringify({ task: "data"}))
才能使其正常工作
答案 0 :(得分:2)
虽然我不确定这是唯一的问题,但似乎您发送的请求正文包含错误的序列化(双重编码)有效负载。
var obj = { foo: 'bar'};
JSON.stringify(obj)返回一个字符串......
'{"foo": "bar"}' // the ' are not part of the string, I'm using them to illustrate that this is a thing of type string.
...当使用JSON解析器解析时,此返回一个对象。
{ foo: 'bar' }
但是,JSON.stringify(JSON.stringify(obj))返回不同的字符串......
'"{\"foo\": \"bar\"}"'
...但在解析时,此会返回一个字符串。
'{"foo": "bar"}'
服务端点期望解析主体并获取对象,而不是字符串......因此,解析请求主体(从服务的角度来看)不会返回正确的类型。该错误似乎是服务无法以非常低的级别解析您的请求。
在您的代码中,body: JSON.stringify(data)
应该只是body: data
,因为之前,您已经使用data: JSON.stringify(payload)
创建了一个JSON对象。
如上所述,您正在有效地将body
设置为JSON.stringify(JSON.stringify(payload))。
答案 1 :(得分:1)
不确定您是否曾想过,但在搜索操作方法时,此问题会在Google上弹出。我想您缺少的一件事是“记录数据”字段必须是base64编码的。这是一部分NodeJS代码(使用PutRecords可以做到这一点)。
对于任何询问的人,为什么不只使用SDK?目前,由于其他依赖关系,我必须从无法将其更新为SDK所需的NodeJS版本的群集中流式传输数据。是的。
const https = require('https')
const aws4 = require('aws4')
const request = function(o) { https.request(o, function(res) { res.pipe(process.stdout) }).end(o.body || '') }
const _publish_kinesis = function(logs) {
const kin_logs = logs.map(function (l) {
let blob = JSON.stringify(l) + '\n'
let buff = Buffer.from(blob, 'binary');
let base64data = buff.toString('base64');
return {
Data: base64data,
PartitionKey: '0000'
}
})
while(kin_logs.length > 0) {
let data = JSON.stringify({
Records: kin_logs.splice(0,250),
StreamName: 'your-streamname'
})
let _request = aws4.sign({
hostname: 'kinesis.us-west-2.amazonaws.com',
method: 'POST',
body: data,
path: '/?Action=PutRecords',
headers: {
'Content-Type': 'application/x-amz-json-1.1',
'X-Amz-Target': 'Kinesis_20131202.PutRecords'
},
}, {
secretAccessKey: "****",
accessKeyId: "****"
// sessionToken: "<your-session-token>"
})
request(_request)
}
}
var logs = [{
'timeStamp': new Date().toISOString(),
'value': 'test02',
},{
'timeStamp': new Date().toISOString(),
'value': 'test01',
}]
_publish_kinesis(logs)