一个快速、极简的 Dart 后端框架?
由 Very Good Ventures ? 开发 ?
实验性 ?
Dart Frog 是一个正在开发中的实验性项目,目前不应在生产环境中使用。
快速开始 ?
先决条件 ?
为了使用 Dart Frog,您必须在您的机器上安装 Dart SDK。
安装 ??
# ? Install the dart_frog cli from source
dart pub global activate dart_frog_cli
创建项目 ✨
使用 dart_frog create 命令创建新项目。
# ? Create a new project called "my_project"
dart_frog create my_project
启动开发服务器 ?
接下来,打开新创建的项目并通过以下方式启动开发服务器:
# ? Start the dev server
dart_frog dev
创建生产构建 ?
创建生产构建,其中包含一个 DockerFile,以便您可以部署到任何地方
# ? Create a production build
dart_frog build
目标 ?
Dart Frog 构建在 shelf 和 mason 之上,并受到 remix.run、next.js 和 express.js 等许多工具的启发。
Dart Frog 的目标是帮助开发人员有效地在 Dart 中构建后端。目前,Dart Frog 专注于优化聚合、组合和规范化来自多个数据源的数据的后端构建过程。Dart Frog 提供了一个具有小型 API 表面的简单核心,以降低开发人员的学习曲线和上手时间。此外,Dart Frog 旨在通过拥有一个统一的技术栈,使 Flutter/Dart 开发人员能够最大化他们的生产力,从而共享工具、模型等!
功能集 ✨
✅ 热重载 ⚡️
✅ Dart Dev Tools ⚙️
✅ 文件系统路由 ?
✅ 索引路由 ?
✅ 嵌套路由 ?
✅ 动态路由 ?
✅ 中间件 ?
✅ 依赖注入 ?
✅ 生产构建 ?♂️
✅ Docker ?
? 生成的 Dart 客户端包 ?
? 生成的 API 文档 ?
文档 ?
路由 ?
在 Dart Frog 中,路由由 `routes` 目录中 `*.dart` 文件导出的 `onRequest` 函数(称为路由处理程序)组成。每个端点根据其文件名与路由文件关联。名为 `index.dart` 的文件将对应于 `/` 端点。
例如,如果您创建 `routes/hello.dart` 并像下面一样导出 `onRequest` 方法,它将可以在 `/hello` 访问。
import 'package:dart_frog/dart_frog.dart';
Response onRequest(RequestContext context) {
return Response(body: 'Hello World');
}
所有路由处理程序都可以访问 `RequestContext`,它可用于访问传入的请求以及请求上下文中提供的依赖项(请参阅中间件)。
import 'package:dart_frog/dart_frog.dart';
Response onRequest(RequestContext context) {
// Access the incoming request.
final request = context.request;
// Return a response.
return Response(body: 'Hello World');
}
路由处理程序可以是同步的或异步的。要将上面的路由处理程序转换为异步,我们只需将返回类型从 `Response` 更新为 `Future<Response>`。我们可以添加 `async` 关键字以在返回 `Response` 之前在我们的处理程序中 `await` futures。
import 'package:dart_frog/dart_frog.dart';
Future<Response> onRequest(RequestContext context) async {
final result = await _someFuture();
return Response(body: 'Result is: $result!');
}
动态路由 ?
Dart Frog 支持动态路由。例如,如果您创建一个名为 `routes/posts/
路由参数会像下面这样传递给 `onRequest` 方法。
import 'package:dart_frog/dart_frog.dart';
Response onRequest(RequestContext context, String id) {
return Response(body: 'post id: $id');
}
中间件 ?
Dart Frog 中的中间件允许您在请求处理之前和之后执行代码。您可以修改传入的请求和传出的响应,提供依赖项等等!
在 Dart Frog 中,中间件是由 `routes` 文件夹子目录中的 `_middleware.dart` 文件导出的 `middleware` 函数。每个路由目录最多只能有一个中间件,其中 `routes/_middleware.dart` 是为所有传入请求执行的中间件。
import 'package:dart_frog/dart_frog.dart';
Handler middleware(Handler handler) {
return (context) async {
// Execute code before request is handled.
// Forward the request to the respective handler.
final response = await handler(context);
// Execute code after request is handled.
// Return a response.
return response;
};
}
我们可以通过 `use` API 链接内置中间件,例如 `requestLogger` 中间件。例如,如果我们创建 `routes/_middleware.dart` 并包含以下内容,我们将自动记录我们服务器的所有请求。
import 'package:dart_frog/dart_frog.dart';
Handler middleware(Handler handler) {
return handler.use(requestLogger());
}
依赖注入 ?
中间件还可以通过 `provider` 为 `RequestContext` 提供依赖项。
`provider` 是一种中间件,可以创建并向请求上下文提供类型为 `T` 的实例。`create` 回调会被惰性调用,并且注入的 `RequestContext` 可用于执行其他查找,以访问上游提供的较高值。
在下面的示例中,我们将使用 `provider` 将 `String` 注入到我们的请求上下文中。
import 'package:dart_frog/dart_frog.dart';
Handler middleware(Handler handler) {
return handler
.use(requestLogger())
.use(provider<String>((context) => 'Welcome to Dart Frog!'));
}
之后,我们可以使用 `context.read<T>()` 从路由处理程序内部访问提供的。
import 'package:dart_frog/dart_frog.dart';
Response onRequest(RequestContext context) {
final greeting = context.read<String>();
return Response(body: greeting);
}
测试 ?
在 Dart Frog 中,我们可以有效地单元测试我们的路由处理程序和中间件,因为它们是纯函数。
例如,我们可以使用 `package:test` 来测试上面的路由处理程序
import 'dart:io';
import 'package:dart_frog/dart_frog.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
import '../../routes/index.dart' as route;
class _MockRequestContext extends Mock implements RequestContext {}
void main() {
group('GET /', () {
test('responds with a 200 and greeting.', () async {
const greeting = 'Hello World!';
final context = _MockRequestContext();
when(() => context.read<String>()).thenReturn(greeting);
final response = route.onRequest(context);
expect(response.statusCode, equals(HttpStatus.ok));
expect(response.body(), completion(equals(greeting)));
});
});
}
在上面的测试中,我们使用 `package:mocktail` 来创建一个模拟的 `RequestContext`,并在调用 `context.read<String>()` 时模拟返回值。然后,我们只需要用模拟的上下文调用 `onRequest`,就可以断言响应是我们期望的。在本例中,我们正在检查 `statusCode` 和 `response body`,以确保响应是 200 状态码,并带有提供的问候语。