我正在尝试通过开发一个简单的应用程序来学习如何结合使用rails和graphql来创建Rails API,该应用程序仅从数据库中检索文本(在我的情况下为引号)并将其显示在屏幕上。我在前端使用flutter,并在graphql作为后端使用rails。后端部分很容易创建,因为我已经有了一些Rails知识,但是前端部分是我的新手,我试图找出如何访问通过flutter创建的graphql查询以获取需要的数据显示。
下面是我当前拥有的颤振代码(部分改编自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();
},
),
),
),
);
}
}
该代码错误的一些显而易见的原因,我已经弄清楚自己是因为我的代码发送get请求时graphql服务器期望查询的后发请求,但这是我的问题。 如何在flutter中向我的graphql服务器发送发布请求以检索数据?我要访问的查询是flutter代码中'?query ='之后的查询。>
答案 0 :(得分:2)
这也花了我一分钟才弄明白,但这是我在练习待办事项应用程序中所做的:
1 - 阅读 this page on graphql post requests over http。有一个部分用于 GET
请求以及 POST
。
2 - 确保您的 body
函数参数是正确的 json 编码(请参阅下面的代码)。
提示:使用 Postman,您可以测试具有不同标头和授权令牌以及请求正文的 graphql 端点。它还具有从请求生成代码的简洁功能。查看this page for details。这不是 100% 准确,但这帮助我弄清楚如何正确设置请求正文的格式。在函数 post
中,如果您提供 Map 作为请求的正文(并且请求内容类型为 application/json
),显然您无法更改内容类型,因此 String 对我有用用例。
示例代码(使用 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);
}
}