javascript - 如何以编程方式连接到 AWS websocket API Gateway
问题描述
我正在尝试通过利用 AWS 的 websocket api 网关在我的网站上实现用户之间的消息传递。我看到的每个指南/文档都说使用 wscat 来测试与网关的连接。我现在可以连接到 api 网关并使用 wscat 在客户端之间发送消息,但我正在努力让它从我的 ts 代码以编程方式工作。
我想要做的是在用户登录后对 websocket api 网关进行 api 调用,以便他们可以随时发送消息。我在后端使用无服务器,在前端使用 Angular 6。我读到我需要POST
请求https://{api-id}.execute-api.us-east-1.amazonaws.com/{stage}/@connections/{connection_id}
通过 websocket 连接发送消息,但是在我创建的用于连接/获取连接 ID 的服务中使用 typescript 时遇到问题。
在用户成功登录以打开与 websocket api 网关的连接后,我正在进行第二次 API 调用。我尝试调用一个函数,该函数发出没有正文的发布请求(不确定我会在请求正文中发送什么,因为我只使用 wscat 工具连接到它)到部署无服务器代码后获得的 URL。在手动部署 API 网关后,我还尝试向我在 AWS 控制台中看到的 https:// URL 发出 POST 请求。
基础服务.ts
protected getBaseSocketEndpoint(): string {
// 'wss://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev' <-- tried this too
return 'https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/@connections';
}
身份验证.service.ts
this.authService.login(username, password).pipe(first()).subscribe(
(response) => {
console.log(response);
this.authService.setCookie('userId', response.idToken.payload.sub);
this.authService.setCookie('jwtToken', response.idToken.jwtToken);
this.authService.setCookie('userEmail', response.idToken.payload.email);
this.authService.setCookie('refreshToken', response.refreshToken.token);
this.toastr.success('Login successful. Redirecting to your dashboard.', 'Success!', {
timeOut: 1500
});
this.authService.connectToWebSocket(response.idToken.payload.sub).pipe(first()).subscribe(
response => {
console.log(response);
}
);
this.routerService.routeToUserDashboard();
},
(error) => {
// const errorMessage = JSON.parse(error._body).message;
this.toastr.error("Incorrect username and password combination.", 'Error!', {
timeOut: 1500
});
}
);
authentication.service.ts 扩展 BaseService
public connectToWebSocket(userId: string): Observable<any> {
const body = {
userId: userId
};
console.log('calling connectToWebSocket()..');
return this.http.post(this.getBaseSocketEndpoint(), body).pipe(map(response => {
console.log(response);
}));
}
无服务器.yaml
functions:
connectionHandler:
handler: connectionHandler.connectionHandler
events:
- websocket:
route: $connect
cors: true
- websocket:
route: $disconnect
cors: true
defaultHandler:
handler: connectionHandler.defaultHandler
events:
- websocket:
route: $default
cors: true
sendMessageHandler:
handler: messageHandler.sendMessageHandler
events:
- websocket:
route: sendMessage
cors: true
connectionHandler.js (lambda)
const success = {
statusCode: 200,
headers: { "Access-Control-Allow-Origin": "*" },
body: "everything is alright"
};
module.exports.connectionHandler = (event, context, callback) => {
var connectionId = event.requestContext.connectionId;
if (event.requestContext.eventType === "CONNECT") {
addConnection(
connectionId,
"b72656eb-db8e-4f32-a6b5-bde4943109ef",
callback
)
.then(() => {
console.log("Connected!");
callback(null, success);
})
.catch(err => {
callback(null, JSON.stringify(err));
});
} else if (event.requestContext.eventType === "DISCONNECT") {
deleteConnection(
connectionId,
"b72656eb-db8e-4f32-a6b5-bde4943109ef",
callback
)
.then(() => {
console.log("Disconnected!");
callback(null, success);
})
.catch(err => {
callback(null, {
statusCode: 500,
body: "Failed to connect: " + JSON.stringify(err)
});
});
}
};
// THIS ONE DOESNT DO ANYHTING
module.exports.defaultHandler = (event, context, callback) => {
callback(null, {
statusCode: 200,
body: "default handler was called."
});
};
const addConnection = (connectionId, userId, callback) => {
const params = {
TableName: CHATCONNECTION_TABLE,
Item: {
connectionId: connectionId,
userId: userId
}
};
var response;
return dynamo
.put(params, function(err, data) {
if (err) {
errorHandler.respond(err, callback);
return;
} else {
response = {
statusCode: 200,
headers: { "Access-Control-Allow-Origin": "*" },
body: JSON.stringify(data)
};
callback(null, response);
}
})
.promise();
};
const deleteConnection = (connectionId, userId, callback) => {
const params = {
TableName: CHATCONNECTION_TABLE,
Key: {
connectionId: connectionId,
userId: userId
}
};
var response;
return dynamo
.delete(params, function(err, data) {
if (err) {
errorHandler.respond(err, callback);
return;
} else {
response = {
statusCode: 200,
headers: { "Access-Control-Allow-Origin": "*" },
body: JSON.stringify(data)
};
callback(null, response);
}
})
.promise();
};
预期:触发 POST api 调用并打开与 Websocket API 网关的持久连接。
实际:无法通过上面的 API 调用进行连接。我在控制台中收到 403 消息:
从源“ http://localhost:4200 ”访问“ https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/@connections ”的 XMLHttpRequest已被 CORS 策略阻止:响应预检请求未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。
当我在无服务器文件中启用 CORS 时,不确定为什么会出现 CORS 错误。
解决方案
我有一个类似的问题。您可以使用所有的 socket.io-client 优点。但是您必须将选项传输设置为:
let socket = io(url, {
reconnectionDelayMax: 1000,
transports: ["websocket"],
});
默认是
transports: ["polling", "websocket"],
因此,您的应用将启动轮询并导致 CORS 错误。文档中不是很清楚,但这里有一个有用的链接。
查看“底层 Engine.IO 客户端的可用选项:”。
推荐阅读
- android - 在 Android 上实现推送通知时出错
- javascript - 如何自动关闭 Materialise css 移动导航栏?
- java - 添加导致在步骤定义中传递不同值的步骤
- python - 时间序列中协方差矩阵的逆
- terraform - 从通过 terraform kubernetes manifest 创建的服务获取外部 IP
- sql-server - 带参数 PowerBI 的 DirectQuery
- docker - 为什么 docker 不能将 /bin/sh -c 识别为有效的入口点?
- spring - StepExecutionListener 没有输入内容,而 SkipPolicy 没有 jobExecutionContext
- javascript - 加载附加的 SVG 文件中包含的 javascript 库
- javascript - 反应原生如何在我的应用程序运行时显示徽章