Dart Frog 实战系列(二):从零构建 REST API(Todo 应用全增删改查)
嘿,大家好!欢迎回到我们的 Dart Frog 实战系列第二期。如果你还没看过[第一期],建议先去补补课——在那一集里,我们完成了 Dart Frog 的环境搭建,并亲手跑通了一个支持热重载的基础 API。
今天,我们要玩点硬核的:我们将不再停留在基础阶段,而是要亲手构建你的第一个“实战级”REST API —— 一个基于纯 Dart 开发、架构整洁且生产就绪的 Todo 全功能后端。
欢迎关注我的微信公众号:OpenFlutter
我们会深入探讨动态路由、UUID 生成、数据校验以及规范的错误处理。在本篇结束时,你将拥有一套完整的、可测试的 API 接口,为下一期视频中对接 Flutter 应用做好全副武装。话不多说,我们直接开搞!
规划与最佳实践
核心思路: 我们将创建一个 Todo 模型,包含 ID、标题(Title)和完成状态(Completed status)。我们会先将数据存储在内存中(使用 Map 以实现快速查找)——这种方式非常适合上手学习,而且以后想要升级到 Postgres 或 Drift 等数据库也易如反掌。
我们要遵循的开发规范:
-
动态路由:利用
[id].dart实现灵活路径。 - UUID 库:确保每一个任务都有唯一的身份证。
- 请求体校验:对接收到的 JSON 数据进行严格检查。
- 规范的状态码:准确返回 200(成功)、201(已创建)、404(未找到)和 400(请求错误)。
准备工作: 打开你在第一部分中创建的项目,或者直接运行 dart_frog create todo_api 新建一个。别忘了启动 dart_frog dev。
第一步,引入 UUID 依赖。编辑 pubspec.yaml:
dependencies:
uuid: ^4.5.0
运行 flutter pub get(或者 dart pub get)。
第一步:创建数据模型 lib/src/todo.dart:
///
class Todo {
///
Todo({required this.id, required this.title, this.isCompleted = false});
/// fromJson
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
id: json['id'] as String,
title: json['title'] as String,
isCompleted: json['isCompleted'] as bool? ?? false,
);
}
/// id
final String id;
/// title
final String title;
/// isCompleted
bool isCompleted;
/// toJson
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'isCompleted': isCompleted,
};
}
}
第二步:内存存储实现 lib/src/todo_repository.dart:
import 'package:my_project/src/todo_model.dart';
import 'package:uuid/uuid.dart';
const _uuid = Uuid();
final _todos = <String, Todo>{};
/// get all todos
List<Todo> getAllTodos() => _todos.values.toList();
/// get a tod
Todo? getTodoById(String id) => _todos[id];
/// create
void createTodo(String title) {
final id = _uuid.v4();
_todos[id] = Todo(id: id, title: title);
}
/// update
void updateTodo(String id, {String? title, bool? isCompleted}) {
final todo = _todos[id];
if (todo == null) return;
_todos[id] = Todo(
id: id,
title: title ?? todo.title,
isCompleted: isCompleted ?? todo.isCompleted,
);
}
/// delete
void deleteTodo(String id) => _todos.remove(id);
现在开始撸路由!
集合接口:routes/todos/index.dart:
import 'package:dart_frog/dart_frog.dart';
import 'package:my_project/src/todo_repository.dart';
Future<Response> onRequest(RequestContext context) async {
switch (context.request.method) {
case HttpMethod.get:
final todos = getAllTodos();
return Response.json(body: todos.map((e) => e.toJson()).toList());
case HttpMethod.post:
final body = await context.request.json() as Map<String, dynamic>;
final title = body['title'] as String?;
if (title == null || title.isEmpty) {
return Response(statusCode: 400, body: 'Title is required');
}
createTodo(title);
return Response(statusCode: 201, body: 'Todo created');
case HttpMethod.delete:
case HttpMethod.put:
case HttpMethod.patch:
case HttpMethod.head:
case HttpMethod.options:
return Response(statusCode: 405);
}
}
动态单项接口:routes/todos/[id].dart:
import 'package:dart_frog/dart_frog.dart';
import 'package:my_project/src/todo_repository.dart';
Future<Response> onRequest(RequestContext context, String id) async {
final todo = getTodoById(id);
if (todo == null) return Response(statusCode: 404);
switch (context.request.method) {
case HttpMethod.get:
return Response.json(body: todo.toJson());
case HttpMethod.put:
final body = await context.request.json() as Map<String, dynamic>;
final title = body['title'] as String?;
final isCompleted = body['isCompleted'] as bool?;
updateTodo(id, title: title, isCompleted: isCompleted);
return Response.json(body: getTodoById(id)!.toJson());
case HttpMethod.delete:
deleteTodo(id);
return Response(statusCode: 204);
case HttpMethod.post:
case HttpMethod.patch:
case HttpMethod.head:
case HttpMethod.options:
return Response(statusCode: 405);
}
}
测试与总结(演示 curl/Postman)
代码撸完了,是时候看看成果了。我们将通过几个快速测试来验证接口是否按预期工作。
curl http://localhost:8080/todos
curl -X POST http://localhost:8080/todos -H "Content-Type: application/json" -d '{"title": "Learn Dart Frog"}'
curl http://localhost:8080/todos/<generated-id>
优雅地处理错误。这套架构将是你构建“生产就绪”后端的坚实地基!
源码地址 👇 —— 如果对你有帮助,请给仓库点个星 ⭐ (Star) 并在社交平台关注我 😄! github.com/techwithsam…
这就是你的第一个实战级 Dart Frog REST API —— 恭喜通关!下一站:我们将亲手把 Flutter App 对接到这套后端上。