原文作者:suragch.medium.com/
发布时间:2020年12月5日-8分钟阅读
无框架的服务器端Dart系列中的第1部分。
我真的很喜欢能够使用和我写Flutter应用时一样的语言来编写服务器代码。做到这一点的两个主要框架是Aqueduct和Angel。不幸的是,Angel正在被废弃,Aqueduct已经有一段时间没有发布稳定的版本了(截至2020年12月)。
既然A的都出来了,那就到了B计划的时候了。
框架虽好,但总有一点魔力。作为Dart核心库之一的dart:io库,已经包含了制作HTTP服务器所需的低级类和函数。所以本文将教你如何自己创建这样一个服务器。
如果成功的话,这可能会变成一系列的文章。或者如果Aqueduct或者其他Dart框架重新上线,那么我可能会给你指出这个方向。
让我们开始吧。完整的代码在文章的最后,如果你迷路的话。
设置
我想你已经安装了Dart。我正在使用Dart 2.12,这样我就可以习惯使用不可空类型进行编码。目前它自带Flutter的测试版。
1 | sh复制代码flutter channel beta |
或者使用测试版的Dart SDK。
现在在命令行中创建一个新的Dart项目,名为my_server(或任何你喜欢的)。
1 | sh复制代码dart create my_server |
用你喜欢的IDE打开那个文件夹。VS Code和IntelliJ都有一个Dart插件。如果你还没有安装,请安装它。
创建一个服务器
用下面的代码替换my_server.dart。
1 | dart复制代码import 'dart:io'; |
这将创建一个监听本地主机IP地址(127.0.0.1)、端口为4040的服务器。
你可以像这样从终端启动你的服务器。
1 | dart复制代码dart run bin/my_server.dart |
你应该看到以下的打印输出。
1 | arduino复制代码Server started: InternetAddress('127.0.0.1', IPv4) port 4040 |
恭喜你!你已经做了一个Dart服务器。你已经建立了一个Dart服务器。这很容易,不是吗?
你已经启动了服务器,但是它还没有真正做任何事情。按Control+C键强制关闭你的程序。
处理HTTP请求
HTTP请求包括GET、POST、PUT和DELETE等。如果你对它们不熟悉,那么你可以在Becoming a backend developer - Part 1: 基础概念阅读更多。
用下面的代码替换主函数。
1 | dart复制代码Future<void> main() async { |
并在my_server.dart中添加handleRequests作为顶层函数。
1 | dart复制代码Future<void> handleRequests(HttpServer server) async { |
注释。
- HttpServer类实现了
Stream<HttpRequest>
。这意味着你可以把它作为一个流来处理一个个进来的请求。 - 你的handleRequests方法会忽略请求的类型。它只是对所有的东西给出相同的响应:首先写一个字符串(将在响应体中返回),然后关闭响应,将其发回给请求者。
再次运行你的程序,然后在浏览器中打开以下地址。
你应该看到以下结果。
你的浏览器为此发出了一个GET请求。接下来你将看到如何路由不同类型的请求。
路由不同类型的请求
我们不要对每个请求都一视同仁,而是按类型进行路由。用下面的代码替换handleRequests方法。
1 | dart复制代码Future<void> handleRequests(HttpServer server) async { |
现在,对于每一个进来的请求,你都要把它路由到一个不同的方法。接下来的几节将探讨如何处理GET、POST和其他任何方法。
我们将首先实现handleGet,所以暂时注释掉handlePost和handleDefault。
1 | arduino复制代码 case 'POST': |
处理GET请求
在my_server.dart中添加以下顶层代码。
1 | dart复制代码var myStringStorage = 'Hello from a Dart server'; |
注释。
- myStringStorage全局变量在这里代表一个数据库。我们在这里从这个变量中读取数据,并将在下一节中向它写入数据。
- GET请求不应该改变服务器状态,所以我们只需要在响应中传回myStringStorage的值。
保存你的工作,重新启动服务器。然后在浏览器中再次打开以下地址。
你应该看到和之前一样的结果。
处理POST请求
POST请求的目的是向服务器添加新资源。
将handleRequests中的handlePost行取消标注,然后在my_server.dart中添加以下顶层函数。
1 | dart复制代码Future<void> handlePost(HttpRequest request) async { |
你还需要添加以下导入。
1 | dart复制代码import 'dart:convert'; |
注释:
- handlePost主体中的第一行从请求中获取传入的数据块,将它们转换为UTF-8格式的字符串,并将它们连接成一个单一的字符串。
- 一旦有了这个字符串,这个方法就用它来更新全局变量myStringStorage的值。这象征着向数据库写入数据。
- 因为我们实际上只是更新一个现有的值,而不是创建一个新的值,所以定义REST API使用PUT而不是POST可能更有意义。但对于我们的例子来说,POST是可以的。
- 还没有任何安全性。如果你把这个服务器放在网上,世界上任何人都可以更新myStringStorage。在未来的文章中,我想谈谈认证和授权。现在你可以阅读服务器端Dart的认证。
保存你的工作并重新启动服务器。
你不能用浏览器进行POST请求,所以你需要另一种工具,比如curl或Postman。你可以通过阅读这篇文章来了解这些和其他的方法来进行POST请求。
我将使用Postman来进行POST请求。打开Postman,执行以下步骤。
- 选择POST作为请求类型。
- 在地址栏中写上http://localhost:4040。
- 选择Body选项卡。
- 写上任何字符串,例如Hello。
- 点击发送按钮。
你应该会收到来自服务器的200 OK响应,并在响应的正文中写上Got it. Thanks.在响应的正文中。
在Postman中看到的来自服务器的响应
如果你运行另一个GET请求(无论是在你的浏览器或Postman),你应该看到myStringStorage的更新值。
很好!你成功地从客户端更新了服务器。
处理其他请求
客户端还可以发出很多其他的HTTP请求–比如PUT、PATCH、DELETE等等。你可以在你的路由器中添加更多的方法来处理它们中的任何一个。然而,在这里我们只想说,在我们的服务器上不允许有其他请求。
取消对handleDefault这一行的标注,然后添加下面的顶层方法。
1 | dart复制代码void handleDefault(HttpRequest request) { |
注释:这次你将状态码设置为methodNotAllowed。
- 这次你把状态代码设置为 methodNotAllowed. 这相当于一个405的代码。
- 你在响应中写一个错误,然后把它发回给客户端。
保存你的工作并重新启动服务器。
在Postman中发送一个PUT请求来测试它。
Postman中看到的服务器错误响应
干得好 Good work. 你已经为建立自己的服务器开了个好头。你会在下面找到完整的代码。不过首先,看看接下来要采取的一些步骤。
继续
你可以到官方的Dart服务器文档中详细阅读本文所涉及的大部分概念。你也应该看看Dart团队维护的http_server和shelf包。
除非你只支持少量公共数据的简单GET请求,否则在你使用Dart作为一个真正的应用的后端之前,有几个主要的缺失部分需要解决。Dart是一个真正的应用程序的后端:
- 数据库。你需要能够从Dart与数据库进行通信,以存储和检索资源。
- 认证:你需要能够从Dart与数据库进行通信以存储和检索资源。你需要能够隐藏私人数据,只允许授权用户更新服务器上的资源。
- 部署:你需要能够隐藏私人数据,并且只允许授权用户更新服务器上的资源。在你的本地机器上运行服务器是很好的,但它最终需要从外部世界访问。
虽然在技术上并不是必须的,但以下主题也会是有用的知道。
- 测试: 如果你不测试你的服务器代码,你就不能确定做一个改变不会破坏它。
- 文件。你要返回给客户的可能不仅仅是数据库中的字符串。最终你也需要服务于文件。
- 并发性。如果你的服务器有一个以上的核心,你可能会使用它。为此你要在另一个隔离区上启动你的服务器。
- CI/CD:手动上传服务器代码的变化,过一段时间就会变得有点老。如果能建立一个系统,在服务器有变化时自动运行测试和更新,那就更好了。
没有承诺,但我想继续写关于这些主题的文章,这样你就可以学会如何自己做这些事情,而不需要依赖一个框架。但即使你真的使用了框架,知道事情是如何工作的,也会让你更有效率。
完整的代码
1 | dart复制代码import 'dart:convert'; |
本文转载自: 掘金