我以为后端就是web后端,后来我才知道这叫服务(器)端。

我以为后端就是黑窗程序,现在我明白它是一种特别的存在。

从基础的dart:io开始

接下来将会展示如何创建dart项目,如何执行,如何调试。

安装环境

参考flutter环境的安装,flutter会自带的安装上dart最新版本

项目构建

作为一个flutter项目,你将会执行以下命令:

  1. flutter create projuct_name --org com.xxxdomain -a kotlin -i swift --version 0.0.1 --description "this is a flutter projuct description."

一般不会真的输入完整的命令,这里解释一下

  1. `create` 后面默认带了一个 `--project-name` 的参数用于指定项目名字,所以这里直接跟了一个 `projuct_name` ,注意:这个项目名一定是符合dart的包名的格式的蛇形命名法
  2. `--org` 后跟域名,最好符合安卓软件包名规范。一般会指定域名,默认是 `com.example`
  3. `-a -i` 用于指定安卓和iOS下的技术栈,可以通过--help查看默认类型,然后根据需求来指定技术栈。
  4. `--version` 默认是 1.0.0 但是我喜欢从 0.0.1 开始
  5. `--description` 后期可以改,没有必要在这里写

所以最后只剩下

  1. flutter create projuct_name --org com.lilua --version 0.0.1

那么作为一个dart项目,也是很乖巧的和flutter神似,这是我没想到,但是又在情理之中的。

  1. dart create projuct_name

并且dart的默认不带版本号,可以自己在 pubspec.yaml 文件中去设置

执行一个dart项目

  1. dart run

或者单独执行某一个dart文件

  1. dart bin/xx_module.dart

调试dart项目

dart是一个支持JIT的语言,可以实时热更新,也可以AOT编译好了再执行,提高运行效率。
参考https://www.jianshu.com/p/9567f798a353可以得到一个服务端热重载的实例。
调试和其他语言无异。打断点然后debug即可。

服务端 hello wold

将以下代码复制到main.dart里替换原来的代码。

  1. import 'dart:io';
  2. void main() async {
  3. var requestServer = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);
  4. print('监听 localhost 地址,端口号为${requestServer.port}');
  5. //监听请求
  6. await for (HttpRequest request in requestServer) {
  7. //监听到请求后response回复它一个Hello World!然后关闭这个请求
  8. var httpResponse = request.response..write('Hello World!');
  9. await httpResponse.close();
  10. }
  11. }

然后在浏览器中输入 http://localhost:8080/ 就可以看到我们的第一个后端程序结果。

原理

一个简单的请求做完了,我们来看看发生了什么
从main函数进来,后面跟了一个async表示这是一个异步的函数(异步自己补习吧)
通过 HttpServer.bind 来绑定一个本地的8080端口作为请求的路口。
通过await for来解决异步获取到的每一个请求,他们会装在 requestServer 里,一个个的拿给 request 做请求的回复。
执行close结束请求。建议直接在拿到结果后直接切断请求,不要挂着导致多次请求堆叠起来。
(不明白的,直接评论区见,反正我没考虑零基础,毕竟没有出零基础的dart入门)

注意事项

尽量使用自己手敲 dart run 这个不会出问题。次之,你可以使用 vscode 的 runner 但是你也看得出来那是运行单文件的。千万避免使用vscode右上角的运行按钮,到时候运行到后台还没法点退出,需要手动在任务管理器中杀掉。

做得更加安全

为了避免其他类型的请求干扰,我们手动屏蔽掉他们。

  1. import 'dart:io';
  2. /// 对请求分类处理
  3. void handleMessage(HttpRequest request) {
  4. // 方便后期维护
  5. try {
  6. if (request.method == 'GET') {
  7. handleGet(request);
  8. } else if (request.method == 'POST') {
  9. handlePost(request);
  10. } else {
  11. // 返回错误的请求
  12. request.response
  13. ..statusCode = HttpStatus.methodNotAllowed
  14. ..write('have not ${request.method} method!!!')
  15. ..close();
  16. }
  17. } catch (e) {
  18. print('get a $e');
  19. }
  20. }
  21. /// 单独处理post
  22. void handlePost(HttpRequest request) {
  23. /// 语法糖,..表示直接引用原来的对象来继续执行方法,可以连续执行多个方法。
  24. request.response
  25. ..write('post')
  26. ..write('something')
  27. ..close();
  28. }
  29. /// 单独处理get
  30. void handleGet(HttpRequest request) {
  31. request.response
  32. ..write('get')
  33. ..close();
  34. }
  35. void main() async {
  36. var requestServer = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);
  37. //HttpServer.bind(主机地址,端口号)
  38. //主机地址:InternetAddress.loopbackIPv4和InternetAddress.loopbackIPv6都可以监听到
  39. print('监听 localhost地址,端口号为${requestServer.port}');
  40. //监听请求
  41. await for (HttpRequest request in requestServer) {
  42. handleMessage(request);
  43. }
  44. }

对于请求结果的状态值:

  1. abstract class HttpStatus {
  2. //继续
  3. static const int continue_ = 100;
  4. //交换协议
  5. static const int switchingProtocols = 101;
  6. //可以
  7. static const int ok = 200;
  8. //已创建
  9. static const int created = 201;
  10. //认可的
  11. static const int accepted = 202;
  12. //非授权信息
  13. static const int nonAuthoritativeInformation = 203;
  14. //没有内容
  15. static const int noContent = 204;
  16. //重置内容
  17. static const int resetContent = 205;
  18. //部分内容
  19. static const int partialContent = 206;
  20. //多项选择
  21. static const int multipleChoices = 300;
  22. //永久迁移
  23. static const int movedPermanently = 301;
  24. //已发现
  25. static const int found = 302;
  26. //临时迁移
  27. static const int movedTemporarily = 302; // Common alias for found.
  28. //查看其它
  29. static const int seeOther = 303;
  30. //未修改的
  31. static const int notModified = 304;
  32. //使用代理
  33. static const int useProxy = 305;
  34. //暂时重定向
  35. static const int temporaryRedirect = 307;
  36. //请求失败
  37. static const int badRequest = 400;
  38. //没有授权
  39. static const int unauthorized = 401;
  40. //要求付款
  41. static const int paymentRequired = 402;
  42. //被禁止
  43. static const int forbidden = 403;
  44. //未找到
  45. static const int notFound = 404;
  46. //请求方法不允许
  47. static const int methodNotAllowed = 405;
  48. //不接受
  49. static const int notAcceptable = 406;
  50. //需要代理身份认证
  51. static const int proxyAuthenticationRequired = 407;
  52. //请求超时
  53. static const int requestTimeout = 408;
  54. //冲突
  55. static const int conflict = 409;
  56. //过去了
  57. static const int gone = 410;
  58. //长度要求
  59. static const int lengthRequired = 411;
  60. //先决条件失败
  61. static const int preconditionFailed = 412;
  62. //请求实体过大
  63. static const int requestEntityTooLarge = 413;
  64. //请求地址过长
  65. static const int requestUriTooLong = 414;
  66. //非支持的媒体类型
  67. static const int unsupportedMediaType = 415;
  68. //请求范围不可满足
  69. static const int requestedRangeNotSatisfiable = 416;
  70. //期望失败
  71. static const int expectationFailed = 417;
  72. //升级要求
  73. static const int upgradeRequired = 426;
  74. //内部服务器错误
  75. static const int internalServerError = 500;
  76. //未实现
  77. static const int notImplemented = 501;
  78. //网关坏了
  79. static const int badGateway = 502;
  80. //服务不可用
  81. static const int serviceUnavailable = 503;
  82. //网关超时
  83. static const int gatewayTimeout = 504;
  84. //http版本不支持
  85. static const int httpVersionNotSupported = 505;
  86. // 连接超时
  87. static const int networkConnectTimeoutError = 599;
  88. }

可以直接点进 HttpStatus 类里面看看它的实现,dart易学在于,官方代码注释非常漂亮。

从uri中拿到参数

例如:http://localhost:8080/?id=4234
我们修改一下 handleGet 函数

  1. void handleGet(HttpRequest request) {
  2. var value = request.uri.queryParameters['id'];
  3. request.response
  4. ..write('get id is $value')
  5. ..close();
  6. }

就是这样简单,使用 request.uri 拿到地址,使用 queryParameters 来匹配id。这样就拿到了传入的值