[Web翻译]Dart Aqueduct服务器为您的Flutter应用程序 - 第2部分:HTTP请求。

575 阅读9分钟

原文地址:developer.school/flutter-mob…

原文作者:developer.school/author/paul…

发布时间:2020年2月27日 - 9分钟阅读

处理GET、POST、PUT和DELETE请求。

这是七部分系列中的第二部分。

  1. 入门
  2. HTTP请求(你在这里)
  3. 数据库
  4. 测试
  5. 认证
  6. 生产服务器
  7. Flutter客户端应用

介紹

在上一课中,你已经把Dart、Aqueduct和IDE都设置好了。你还从浏览器向运行中的Aqueduct服务器发出了一个简单的GET请求,并将响应返回给浏览器。

在本课中,你将学习一些更多的方法来进行HTTP请求。除了GET请求外,你还会进行POST、PUT和DELETE请求。在服务器端,您将为Aqueduct编写代码来处理这些请求。

发出HTTP请求的方法

如果你喜欢看视频而不是阅读文本,那么这里是下一个视频。

www.youtube.com/watch?v=QGh…

有很多不同的方式 你可以做HTTP请求。除了浏览器,你还可以使用一个叫Postman的程序,命令行实用程序curl,一个叫Swagger的浏览器工具,Aqueduct测试框架,甚至是一个Flutter应用程序

我们将简要地看看每一个,但首先打开你在上一课中创建的Aqueduct项目。在lib/channel.dart文件中,用下面的代码替换 entryPoint()方法。

@override
Controller get entryPoint {
  final router = Router();

  router.route("/example").linkFunction(
    (request) async {
      return Response.ok('Hello world')
        ..contentType = ContentType.text;
    },
  );

  return router;
}

注意:

  • 我修改了响应,以纯文本形式而不是JSON形式返回Hello world
  • 路由的路径仍然是/example

现在,在Dart项目的根目录下,在终端上运行以下命令来启动服务器。

aqueduct serve

浏览器

打开任何一个浏览器,导航到以下地址。

http://localhost:8888/

Aqueduct应该返回404 Not Found响应,因为你没有处理/根路径。现在进入你定义的路由。

http://localhost:8888/example

你应该得到一个Hello world的回应。

浏览器上的成功。

Postman

浏览器的地址栏只适合做基本的GET请求,但你也需要做其他请求。Postman是一个方便的程序。这将是你在本系列中进行HTTP请求的主要工具。

这里下载Postman。如果你继续点击,你不需要注册任何东西。下载好后,安装它。 在Postman地址栏中输入以下内容,然后点击发送

localhost:8888/example

你应该会得到Aqueduct的回应,在正文中显示Hello World

邮递员200号回复OK

注意,状态是200 OK。服务器一切正常。

使用Postman成功。

curl

Curl是一个强大的命令行工具。有时你没有图形用户界面来进行请求(比如当你在服务器上时),所以有一个这样的工具是非常有用的。

打开一个终端,运行下面的命令,看看你是否安装了Curl。

curl --version

如果你没有安装,请随意跳过这一部分。我们不会那么多的使用它。但如果你想安装它,官方网站在这里

要进行GET请求,运行以下命令。

curl -X GET "http://localhost:8888/example"

你应该得到一个Hello world的回应。

我做的截图是小写的H,没好意思修。

使用curl成功。

Swagger

Swagger,现在叫OpenAPI,是一种让你在浏览器中创建客户端应用来测试服务器应用的方法。实际上到目前为止,我还没有发现它有什么用处,所以我现在不打算详细介绍如何使用它。

不过如果你有兴趣的话,这里是官方页面,这里是Aqueduct关于它的文档。

Aqueduct测试框架

Aqueduct测试框架是进行HTTP请求的好方法。你将在第4部分测试中做更多的事情,但现在让我们做一个简短的预览。 在你的项目中,打开文件test/example.dart

在代码上点击右键以显示上下文菜单。选择 "运行",或者按Control Shift R键。

测试失败的原因是,它期望主体是{"key": "value"}。不过我们已经把body改成了Hello world。测试需要更新。

用下面的代码替换整个文件。

import 'harness/app.dart';

Future main() async {
  final harness = Harness()..install();

  test("GET /example returns 200 {'key': 'value'}", () async {
    expectResponse(
      await harness.agent.get("/example"),
      200,
      body: "Hello world",
    );
  });
}

再次运行测试,应该可以通过。

用Aqueduct测试框架成功提出请求。

Flutter应用程序

我说Flutter应用程序,但它可能是一个Android或iOS应用程序。你可以使用这些选项中的任何一个来进行HTTP请求。

我们将在第7部分制作一个Flutter应用,但如果你想先制作一个应用,顺便测试一下服务器,你可以这样做。

你在Flutter中进行HTTP请求的方式是使用http包。你可以在下面的文章中阅读更多的内容。

如何在Flutter中进行HTTP请求

处理GET请求

服务器已经在处理对/example路由的GET请求,但我们现在要扩展这个功能。

www.youtube.com/watch?v=sqh…

在这个系列中,你将构建一个字典应用程序,所以你将定义一个路径为/words的路由,而不是/example

lib/channel.dart中,用下面的方法替换 entryPoint()方法。

@override
Controller get entryPoint {
  final router = Router();

  router.route("/words").link(() => WordsController());

  return router;
}

注意以下几点:

  • 路径是/words.
  • 我们使用linkFunction代替link。这将所有到/words的请求转发到控制器类。这样可以保持我们的路由器代码简洁。
  • 你还没有建立WordsController()类。你将在接下来做。

创建一个控制器

lib文件夹中创建一个名为controller的子文件夹。

然后在lib/controller中创建一个名为word_controller.dart的文件。粘贴以下代码。

import 'package:dart_server/dart_server.dart';

class WordsController extends ResourceController {

  final _words = [
    {'word': 'horse'},
    {'word': 'cow'},
    {'word': 'camel'},
    {'word': 'sheep'},
    {'word': 'goat'},
  ];

  @Operation.get()
  Future<Response> getAllWords() async {
    return Response.ok(_words);
  }
}

注释:

  • 这个类扩展了ResourceController,它是一个Aqueduct类,负责处理请求的很多细节。
  • 我们还没有建立数据库,所以对于数据,我们现在只用一个地图对象的数组。
  • @Operation.get()是一个注解,它告诉Aqueduct这个方法将处理GET请求。它返回一个Response.ok(),这是一个200 OK的响应,我们要把数据列表传进去。Aqueduct会自动将其转换为JSON字符串,并将其放入响应体中。

lib/channel.dart中导入你刚才做的类。

测试你的新控制器

用Control+C停止服务器。然后重新启动服务器。

aqueduct serve

打开Postman,像我们之前那样提出请求,但这次把地址改成如下。

localhost:8888/words

当你点击发送时,你应该在响应体中看到JSON列表。

获取单个项目

目前服务器会返回整个数据列表。如果你只想获取一个数据项怎么办?一个常见的方法是在路径中添加一个ID,比如这样。

localhost:8888/words/1

这是向服务器请求id=1的词,但我们还没有处理。

打开lib/channel.dart。将 entryPoint() 方法替换为以下内容。

@override
Controller get entryPoint {
  final router = Router();

  router.route("/words/[:id]").link(() => WordsController());

  return router;
}

注意:

  • 现在,/words路径中附加了/[:id]
  • :表示id是一个变量。你可以随意调用这个变量。
  • 方括号[ ]表示这个变量是可选的。

回到lib/controller/words_controller.dart。你将创建一个新的方法来处理单项的请求。在getAllWords()方法下面粘贴以下方法。

@Operation.get('id')
Future<Response> getWordByID(@Bind.path('id') int id) async {
  return Response.ok(_words[id]);
}

注意:

  • 注解中的'id'是用来区分这个方法和我们之前的plain@Operation.get()方法的。这里的'id'与我们在channel.dart中使用的变量名称相同。
  • @Bind部分意味着Aqueduct将接受路径中的'id'变量,并尝试将其转换为一个int。如果转换不成功,那么Aqueduct将返回404 Not Found。但如果成功了,那么你可以使用整数作为索引从数据列表中返回一个项目。

重新启动服务器。

aqueduct serve

在Postman中发送一个GET请求,地址如下。

localhost:8888/words/1

你应该得到的回应是,正文中只有一个项目。

索引1的项目被返回。成功! 你可以通过发送不同的索引值来玩转GET请求。如果你选择一个大的值,你会导致服务器错误,Aqueduct会给你一个500的响应。

处理POST、PUT和DELETE请求。

在本节中,我们只是要创建处理POST、PUT和DELETE请求的方法。因为这些请求是为了修改服务器上的数据,所以我们要到第3部分添加数据库时才会完成。

www.youtube.com/watch?v=T-X…

lib/controller/words_controller.dart中为WordsController类添加以下方法。

@Operation.post()
Future<Response> addWord() async {
  return Response.ok(null);
}

@Operation.put('id')
Future<Response> updateWord(@Bind.path('id') int id) async {
  return Response.ok(null);
}

@Operation.delete('id')
Future<Response> deleteWord(@Bind.path('id') int id) async {
  return Response.ok(null);
}

注释:

  • Operation注解标识了该方法处理的请求类型。POST请求用于创建服务器资源。PUT请求是用于更新一个现有资源。DELETE的名字很好听,所以确实需要解释。
  • 由于PUT和DELETE工作在现有数据上,所以我们是将一个ID绑定到一个整数上,以确定要更新或删除哪个数据项。
  • 目前,一切都返回一个默认的200 OK响应。我们稍后会改变这一点。

aqueduct serve重新启动服务器。

在Postman中测试一下

打开Postman。从下拉菜单中选择POST,而不是GET。

在请求的URL中写下以下内容,然后按发送

localhost:8888/words

你应该得到一个200 OK的回复。

使用带有ID的URL重复这个过程进行PUT和DELETE。

localhost:8888/words/1

结果应该还是200 OK

完整代码

如果你迷路了,这里是我们今天所做的完整代码。

lib/channel.dart

import 'package:dart_server/controller/words_controller.dart';

import 'dart_server.dart';

class DartServerChannel extends ApplicationChannel {
  @override
  Future prepare() async {
    logger.onRecord.listen(
        (rec) => print("$rec ${rec.error ?? ""} ${rec.stackTrace ?? ""}"));
  }

  @override
  Controller get entryPoint {
    final router = Router();

    router.route("/words/[:id]").link(() => WordsController());

    return router;
  }
}

lib/controller/words_controller.dart

import 'package:dart_server/dart_server.dart';

class WordsController extends ResourceController {

  final _words = [
    {'word': 'horse'},
    {'word': 'cow'},
    {'word': 'camel'},
    {'word': 'sheep'},
    {'word': 'goat'},
  ];

  @Operation.get()
  Future<Response> getAllWords() async {
    return Response.ok(_words);
  }

  @Operation.get('id')
  Future<Response> getWordByID(@Bind.path('id') int id) async {
    return Response.ok(_words[id]);
  }

  @Operation.post()
  Future<Response> addWord() async {
    return Response.ok(null);
  }

  @Operation.put('id')
  Future<Response> updateWord(@Bind.path('id') int id) async {
    return Response.ok(null);
  }

  @Operation.delete('id')
  Future<Response> deleteWord(@Bind.path('id') int id) async {
    return Response.ok(null);
  }
}

结论

你现在知道如何处理任何请求类型的基本知识了。当然后面还会有更多的内容需要填写,但正如你所看到的,它并不难。在本课中,路径是/words。你可以很容易地处理其他路径,只需定义一些其他路径并分配一个新的控制器来处理它。

在第三部分,你将学习如何将PostgreSQL数据库连接到Aqueduct服务器。

当我完成第三部分后,我会在这里添加一个链接。在此之前,请继续观看系列视频或关注Aqueduct文档

www.twitter.com/FlutterComm


通过( www.DeepL.com/Translator )(免费版)翻译