【问题标题】:How to send post request to graphql API in flutter如何在flutter中向graphql API发送post请求
【发布时间】:2021-01-10 00:55:03
【问题描述】:

我正在尝试学习如何将 rails 与 graphql 结合使用,通过开发一个简单的应用程序来创建 rails API,该应用程序仅从数据库中检索文本(在我的情况下为引号)并将其显示在屏幕上。我使用颤振作为前端和使用graphql 作为后端的rails。后端部分很容易创建,因为我已经掌握了一些 Rails 知识,但前端部分是我的新手,我试图弄清楚如何访问我通过颤振创建的 graphql 查询以获取需要的数据显示出来。

下面是我目前拥有的flutter代码(部分改编自How to build a mobile app from scratch with Flutter and maybe Rails?)。

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<Quote> fetchQuote() async {
  final response =
      await http.get('http://10.0.2.2:3000/graphql?query={quote{text}}');

  if (response.statusCode == 200) {
    // If the call to the server was successful, parse the JSON.
    return Quote.fromJson(json.decode(response.body));
  } else {
    // If that call was not successful, throw an error.
    throw Exception('Failed to load quote');
  }
}

class Quote {
  final String text;

  Quote({this.text});

  factory Quote.fromJson(Map<String, dynamic> json) {
    return Quote(
      text: json['text']
    );
  }
}


void main() => runApp(MyApp(quote: fetchQuote()));

class MyApp extends StatelessWidget {
  final Future<Quote> quote;

  MyApp({this.quote});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder<Quote>(
            future: quote,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data.text);
              } else if (snapshot.hasError) {
                return Text("${snapshot.error}");
              }

              // By default, show a loading spinner.
              return CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

我自己已经发现这段代码错误的一些明显原因是,当我的代码发送获取请求时,graphql 服务器期望查询的发布请求,但这是我的问题。 如何在 Flutter 中向我的 graphql 服务器发送一个发布请求以检索数据?我尝试访问的查询是我的 Flutter 代码中“?query=”之后的查询。 p>

【问题讨论】:

  • 最简单的事情,请查看flutter.dev/docs/cookbook/networking/send-data 了解如何发布 http.post。不太容易,您的服务器似乎配置不正确。获取数据时使用 Get,更改某些内容时使用 Post。看起来您正在正确地尝试获取我可以看到的代码,所以我的猜测是服务器错误。

标签: api flutter graphql http-post


【解决方案1】:

我也花了一分钟才弄清楚,但这是我在练习 todo 应用程序中所做的:

1 - 阅读this page on graphql post requests over httpGET Requests 和 POST 有一个部分。

2 - 确保您的 body 函数参数是正确的 json 编码(参见下面的代码)。

提示:使用 Postman,您可以测试带有不同标头和授权令牌以及请求正文的 graphql 端点。它还具有从请求生成代码的简洁功能。查看this page for details。它不是 100% 准确的,但这正是帮助我弄清楚如何正确格式化请求正文的原因。在函数 post 中,如果您提供 Map 作为请求的主体(并且请求内容类型为 application/json),显然您无法更改内容类型,因此字符串适用于我的用例。

示例代码(使用GqlParser 类对请求正文进行正确编码):

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'todo.dart';
import '../creds/creds.dart';
import 'gql_parser.dart';

const parser = GqlParser('bin/graphql');

class TodoApiException implements Exception {
  const TodoApiException(this.message);
  final String message;
}

class TodoApiClient {
  const TodoApiClient();
  static final gqlUrl = Uri.parse(Credential.gqlEndpoint);
  static final headers = {
    "x-hasura-admin-secret": Credential.gqlAdminSecret,
    "Content-Type": "application/json",
  };

  Future<List<Todo>> getTodoList(int userId) async {
    final response = await http.post(
      gqlUrl,
      headers: headers,
      body: parser.gqlRequestBody('users_todos', {'userId': userId}),
    );

    if (response.statusCode != 200) {
      throw TodoApiException('Error fetching todos for User ID $userId');
    }

    final decodedJson = jsonDecode(response.body)['data']['todos'] as List;
    var todos = <Todo>[];

    decodedJson.forEach((todo) => todos.add(Todo.fromJson(todo)));
    return todos;
  }
// ... rest of class code ommitted

根据.post() 正文参数文档:

如果是字符串,则使用 [encoding] 编码并用作正文 的请求。请求的内容类型将默认为 “文本/纯文本”。

如果 [body] 是一个 List,它被用作 请求。

如果 [body] 是 Map,则使用 [encoding] 将其编码为表单字段。这 请求的内容类型将设置为 “应用程序/x-www-form-urlencoded”;这不能被覆盖。

我在GqlParser 类中使用下面的代码简化了字符串的创建,以作为参数的主体。这将允许您拥有一个文件夹,例如 graphql,其中包含多个 *.graphql 查询/突变。然后,您只需在需要发出简单 graphql 端点请求的其他类中使用 parser,并提供文件名(不带扩展名)。

import 'dart:convert';
import 'dart:io';

class GqlParser {
  /// provide the path relative to of the folder containing graphql queries, with no trailing or leading "/".
  /// For example, if entire project is inside the `my_app` folder, and graphql queries are inside `bin/graphql`,
  /// use `bin/graphql` as the argument.
  const GqlParser(this.gqlFolderPath);

  final String gqlFolderPath;

  /// Provided the name of the file w/out extension, will return a string of the file contents
  String gqlToString(String fileName) {
    final pathToFile =
        '${Directory.current.path}/${gqlFolderPath}/${fileName}.graphql';
    final gqlFileText = File(pathToFile).readAsLinesSync().join();
    return gqlFileText;
  }

  /// Return a json-encoded string of the request body for a graphql request, given the filename (without extension)
  String gqlRequestBody(String gqlFileName, Map<String, dynamic> variables) {
    final body = {
      "query": this.gqlToString(gqlFileName),
      "variables": variables
    };
    return jsonEncode(body);
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-12
    • 2015-01-26
    • 2018-11-02
    • 1970-01-01
    • 2019-03-29
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多