aws appsync订阅不能使用flutter graphql_flutter包



my pubspec.yaml

dev_dependencies:
flutter_test:
sdk: flutter
graphql_flutter: ^4.0.0

roomctrl.dart

import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:innospace/config/appsync-service.dart';
import 'package:innospace/config/client_provider.dart';
class RoomCtrl extends StatefulWidget {
RoomCtrl({Key key}) : super(key: key);
_RoomCtrlState createState() => _RoomCtrlState();
}
class _RoomCtrlState extends State<RoomCtrl> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ClientProvider(
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Subscription(
options:SubscriptionOptions(
document: gql(AppSyncService.onUpdateStateTestSubscription)),
builder: (result) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return Center(
child: const CircularProgressIndicator(),
);
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
child: Text(result.data.length.toString()),
)
],
),
);
},
)
],
),
),
));
}
}

client_provider.dart

import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:flutter/material.dart';
import 'constants.dart';
String uuidFromObject(Object object) {
if (object is Map<String, Object>) {
final String typeName = object['__typename'] as String;
final String id = object['id'].toString();
if (typeName != null && id != null) {
return <String>[typeName, id].join('/');
}
}
return null;
}
ValueNotifier<GraphQLClient> clientFor() {
const dynamic headers = {
"headers": {
"host": AWS_APP_SYNC_ENDPOINT_AUTHORITY,
"x-api-key": AWS_APP_SYNC_KEY
}};
const  sClient= SocketClientConfig(
autoReconnect : true,
initialPayload: headers
);
final WebSocketLink _webSocketLink =new WebSocketLink(AWS_APP_SYNC_ENDPOINT_WSS,  config:sClient );
final Link link = _webSocketLink;
return ValueNotifier<GraphQLClient>(
GraphQLClient(
cache: GraphQLCache(),
link: link,
),
);
}
/// Wraps the root application with the `graphql_flutter` client.
/// We use the cache for all state management.
class ClientProvider extends StatelessWidget {
ClientProvider({
@required this.child,
}) : client = clientFor();
final Widget child;
final ValueNotifier<GraphQLClient> client;
@override
Widget build(BuildContext context) {
return GraphQLProvider(
client: client,
child: child,
);
}
}

constants.dart

[https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html] [1]

const AWS_APP_SYNC_ENDPOINT_AUTHORITY = "xxxxxxxxxxxxxxxxxxxxx.appsync-api.ap-southeast-2.amazonaws.com"; 
const AWS_APP_SYNC_ENDPOINT_WSS = "wss://xxxxxxxxxxxxxxxxxxxxx.appsync-realtime-api.ap-southeast-2.amazonaws.com/graphql?header=base64encryption(xxx-xxxxxxxxxxxxxxxxxx as per aws)&payload=e30="; 
const AWS_APP_SYNC_KEY = "xxx-xxxxxxxxxxxxxxxxxx";

订阅查询

static String onUpdateStateTestSubscription = '''
subscription OnUpdateStateTest {
onUpdateStateTest {
__typename
RoomId
RoomName
}
}''';
控制台

Connecting to websocket: wss://xxxxxxxxxxxxxxxxxxxxx.appsync-realtime-api.ap-southeast-2.amazonaws.com/graphql?header=base64encryption(xxx-xxxxxxxxxxxxxxxxxx as per aws)&payload=e30=...
I/flutter ( 9942): Connected to websocket.
I/flutter ( 9942): Haven't received keep alive message for 30 seconds. Disconnecting..
I/flutter ( 9942): Disconnected from websocket.
I/flutter ( 9942): Scheduling to connect in 5 seconds...
I/flutter ( 9942): Connecting to websocket: wss://xxxxxxxxxxxxxxxxxxxxx.appsync-realtime-api.ap-southeast-2.amazonaws.com/graphql?header=base64encryption(xxx-xxxxxxxxxxxxxxxxxx as per aws)&payload=e30=...
I/flutter ( 9942): Connected to websocket.
I/flutter ( 9942): Haven't received keep alive message for 30 seconds. Disconnecting..
I/flutter ( 9942): Disconnected from websocket.
I/flutter ( 9942): Scheduling to connect in 5 seconds...

最后在移动端输出显示加载gif即CircularProgressIndicator(),意味着返回总是true在"if (result.isLoading)">

[![2]][2]

有谁能帮帮我吗?注意:同样的appsync在angular应用中也能完美地工作。[1]: https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html[2]: https://i.stack.imgur.com/59KZh.png

查看我最近的更新在底部

我实际上遇到了同样的问题。我没有一个完整的答案,但我觉得这是一个部分答案的线索。

从链接:

据我所知,AppSync不使用wss://我有以前试过这个,我的HTTP连接从来没有"升级"到wss"连接。我必须将请求发送到http端点请求订阅连接,然后AWS返回连接包含新端点(WSS url)、主题和客户端的详细信息id。只有这样我才能使用第三方连接到套接字库(使用AWS返回的URL)。我不得不深入调查他们的官方SDK来找出这个,然后通过试错来得到它工作。

我已经确认了graphhiql工具,当我发布我的订阅graphql blob时,我确实得到了响应。我得到一个websocket url返回与JSON中的其他一些项目。我还没有弄清楚如何在Flutter中捕捉到这种反应,但如果我找到了那块,我会更新这个。

从那里,我相信我们需要从这个新url动态地创建websocket,并将我们订阅的graphql blob发送给它。

当然,这很复杂,但这是我现在正在进行的路径。

更新:AWS Amplify for Flutter现在支持订阅!我使用的是混合解决方案。我有flutter_graphqlartemis的突变/查询。对于订阅,我使用amplify_api: '<1.0.0'。它是在过去几周才发布的。实际上我只花了几分钟就把它修好了。Amplify会自动为您完成URL解析和认证。

链接到官方文档

您需要生成一个特定于您的AWS端点的amplifyconfiguration.dart,并将其添加到您的项目中。AWS文档涵盖了这些内容,设置AWS端点的人员应该确切地知道您需要什么。

示例来自链接:

try {
String graphQLDocument = '''subscription OnCreateTodo {
onCreateTodo {
id
name
description
}
}''';
var operation = Amplify.API.subscribe(
request: GraphQLRequest<String>(document: graphQLDocument),
onData: (event) {
print('Subscription event data received: ${event.data}');
},
onEstablished: () {
print('Subscription established');
},
onError: (e) {
print('Subscription failed with error: $e');
},
onDone: () {
print('Subscription has been closed successfully');
});
} on ApiException catch (e) {
print('Failed to establish subscription: $e');
}

别忘了退订:

// Cancel the subscription when you're finished with it
operation.cancel();

这里我要尝试的第一件事是使用@JLuisRojas的自定义AppSync请求序列化器:

class AppSyncRequest extends RequestSerializer {
final Map<String, dynamic> authHeader;
const AppSyncRequest({
this.authHeader,
});
@override
Map<String, dynamic> serializeRequest(Request request) => {
"data": jsonEncode({
"query": printNode(request.operation.document),
"variables": request.variables,
}),
"extensions": {
"authorization": this.authHeader,
}
};
}
// ...
final token = session.getAccessToken().getJwtToken();
String toBase64(Map data) => base64.encode(utf8.encode(jsonEncode(data)));
final authHeader = {
"Authorization": token,
"host": "$apiId.appsync-api.$zone.amazonaws.com",
};
final encodedHeader = toBase64(authHeader);
final WebSocketLink wsLink = WebSocketLink(
'wss://$apiId.appsync-realtime-api.$zone.amazonaws.com/graphql?header=$encodedHeader&payload=e30=',
config: SocketClientConfig(
serializer: AppSyncRequest(authHeader: authHeader),
inactivityTimeout: Duration(seconds: 60),
)
);
final AuthLink authLink = AuthLink(
getToken: () => token,
);
final Link link = authLink.concat(wsLink);

请注意,该连接似乎不仅需要header查询参数,而且还需要在每个请求的extensions字段中进行授权,以及一个非标准的编码结构(docs)。

最新更新