简体   繁体   English

aws appsync 订阅不适用于 flutter graphql_flutter package

[英]aws appsync subscription not working with flutter graphql_flutter package

my pubspec.yaml我的 pubspec.yaml

dev_dependencies:
  flutter_test:
    sdk: flutter
  graphql_flutter: ^4.0.0

roomctrl.dart 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 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常量.dart

[https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html][1] [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";

subscription query订阅查询

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

console安慰

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...

Finally output on mobile is showing loading gif ie CircularProgressIndicator(), means returns always true at "if (result.isLoading)"最后,移动设备上的 output 显示正在加载 gif,即 CircularProgressIndicator(),意味着在“if (result.isLoading)”处始终返回 true

[![enter image description here][2]][2] [![在此处输入图像描述][2]][2]

Could anyone please help!!谁能帮忙!!

note: the same appsync working perfectly in angular application.注意:相同的 appsync 在 angular 应用程序中完美运行。 [1]: https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html [2]: https://i.stack.imgur.com/59KZh.png [1]: https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html [2]: https://i.stack.imgur.com/59KZh.png

See my recent update at the bottom.在底部查看我最近的更新。

I'm actually running into the same problem.我实际上遇到了同样的问题。 I don't have a complete answer but I feel like this is a clue to a partial answer.我没有完整的答案,但我觉得这是部分答案的线索。

From the link : 从链接

As far as I know, AppSync doesn't use wss://<your_end_point>.据我所知,AppSync 不使用 wss://<your_end_point>。 I have tried this before, and my http connection was never "upgraded to a wss" connection.我以前试过这个,我的 http 连接从未“升级到 wss”连接。 I had to POST a request to the http endpoint requesting a subscription connection, then AWS returned connection details containing a new endpoint (a wss url), a topic and a client id.我必须向 http 端点发送请求订阅连接的请求,然后 AWS 返回包含新端点(wss url)、主题和客户端 ID 的连接详细信息。 Only then could I connect to the socket using using a third party library (using the URL AWS returned).只有这样我才能使用第三方库连接到套接字(使用返回的 URL AWS)。 I had to dig into their official sdk to find this out, and then through trial and error to get it to work.我不得不深入研究他们的官方 sdk 来找出这个问题,然后通过反复试验让它工作。

I've confirmed w/ the GraphiQL tool that I do get a response when I POST my subscription graphql blob.我已经使用 GraphiQL 工具确认,当我发布订阅 graphql blob 时确实会收到响应。 I get a websocket url back along w/ some other items in JSON.我得到了 websocket url 和 JSON 中的其他一些项目。 I haven't figured out how to capture that response in Flutter yet but I'll update this if I find that piece as well.我还没有想出如何在 Flutter 中捕获该响应,但如果我也找到该部分,我会更新它。

From there, I believe we'd need to dynamically create the websocket from that new url and send our subscription graphql blob to that.从那里,我相信我们需要从新的 url 动态创建 websocket 并将我们的订阅 graphql blob 发送给它。

Absolutely, this is convoluted but it's the path I'm proceeding with for now.绝对,这是令人费解的,但这是我现在要走的路。

UPDATE: AWS Amplify for Flutter now supports subscriptions.更新:适用于 Flutter 的 AWS Amplify 现在支持订阅。 I am using a hybrid solution.我正在使用混合解决方案。 I have flutter_graphql and artemis for mutations/queries.我有用于突变/查询的flutter_graphqlartemis For subscriptions I am using amplify_api: '<1.0.0' .对于订阅,我使用的是amplify_api: '<1.0.0' It was just released in the past weeks.它在过去几周刚刚发布。 It actually only took me a few minutes to get it working.实际上,我只花了几分钟就让它工作了。 Amplify automatically does the URL resolution and authentication for you. Amplify 会自动为您执行 URL 解析和身份验证。

Link to official docs 链接到官方文档

You'll need to generate an amplifyconfiguration.dart that is specific for your AWS endpoint and add it to your project.您需要生成特定于您的 AWS 终端节点的amplifyconfiguration.dart并将其添加到您的项目中。 The AWS docs cover this and the person that setup your AWS endpoint should know exactly what you need. AWS 文档涵盖了这一点,设置您的 AWS 终端节点的人应该确切地知道您需要什么。

Example from link:来自链接的示例:

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');
}

Don't forget to unsubscribe:不要忘记取消订阅:

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

The first thing I would try here is using @JLuisRojas's custom AppSync request serializer :我在这里尝试的第一件事是使用@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);

Notice that not only does the connection seem to require the header query parameter , but it also requires authorization in the extensions field on each request, and a non-standard encoding structure ( docs ).请注意,连接似乎不仅需要header查询参数,而且还需要在每个请求的extensions字段中授权,以及非标准编码结构 ( docs )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM