我试图通过利用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而创建的服务中使用打字稿遇到了麻烦。>
在用户成功登录以打开与websocket api网关的连接之后,我正在进行第二次API调用。我尝试调用一个没有正文的发布请求的函数(不确定我将在请求正文中发送的内容,因为我仅使用wscat工具将其发送给它)部署了无服务器代码后获得的URL。在手动部署API网关之后,我还尝试过对在AWS控制台中看到的https:// URL发出POST请求。
base.service.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';
}
authentication.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);
}));
}
serverless.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,并显示以下消息:
从原点“ https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/@connections”到“ http://localhost:4200”处对XMLHttpRequest的访问已被CORS策略阻止:对预检请求的响应未通过访问控制检查:否'Access-Control-Allow-来源的标头出现在请求的资源上。
不确定在无服务器文件中启用CORS时为什么会出现CORS错误。
答案 0 :(得分:0)
我遇到了同样的问题,最后弄清楚,通常在websockets中不应出现这样的CORS错误消息:
Why is there no same-origin policy for WebSockets? Why can I connect to ws://localhost?
跳过客户端库“ socket.io”并使用“ vanilla websockets”帮助了我。
在您的情况下,我将检查“ connectToWebSocket”后面的库。
答案 1 :(得分:0)
我有类似的问题。您可以使用所有socket.io-client优点。但是您必须将选项传输设置为:
let socket = io(url, {
reconnectionDelayMax: 1000,
transports: ["websocket"],
});
默认值为
transports: ["polling", "websocket"],
因此,您的应用将开始轮询,并导致CORS错误。在文档中尚不清楚,但是这里是useful link。
在“基础Engine.IO客户端的可用选项:”下查看。
答案 2 :(得分:0)
service: realtime-websocket-test
provider:
name: aws
stage: ${opt:stage, 'dev'}
runtime: python3.8
region: ${opt:region, 'ap-south-1'}
memorySize: 128
timeout: 300
functions:
connect:
handler: handler.lambda_handler
events:
- websocket:
route: $connect
- websocket:
route: $disconnect
- websocket:
route: $default
import time
import json
import boto3
def lambda_handler(event, context):
print(event)
event_type = event["requestContext"]["eventType"]
if event_type == "CONNECT" or event_type == "DISCONNECT":
response = {'statusCode': 200}
return response
elif event_type == "MESSAGE":
connection_id = event["requestContext"]["connectionId"]
domain_name = event["requestContext"]["domainName"]
stage = event["requestContext"]["stage"]
message = f'{domain_name}: {connection_id}'.encode('utf-8')
api_client = boto3.client('apigatewaymanagementapi', endpoint_url = f"https://{domain_name}/{stage}")
for _ in range(5):
api_client.post_to_connection(Data=message,
ConnectionId=connection_id)
time.sleep(5)
response = {'statusCode': 200}
return response