Loading...
Loading...
Compare original and translation side by side
Applies to: Shelf 1.x, Dart 3.x, REST APIs, Microservices, Backend Services Complements:.claude/skills/dart-guide/SKILL.md
适用范围:Shelf 1.x、Dart 3.x、REST API、微服务、后端服务 补充文档:.claude/skills/dart-guide/SKILL.md
PipelineaddMiddlewareaddHandlerHandlerFutureOr<Response> Function(Request)request.change()contextCascadePipelineaddMiddlewareaddHandlerHandlerFutureOr<Response> Function(Request)request.change()contextCascademyapp/
├── bin/
│ └── server.dart # Entry point (thin: config, serve, shutdown)
├── lib/
│ ├── src/
│ │ ├── app.dart # Pipeline + Router assembly
│ │ ├── config/
│ │ │ └── config.dart # Environment-based configuration
│ │ ├── handlers/
│ │ │ ├── health_handler.dart
│ │ │ └── users_handler.dart
│ │ ├── middleware/
│ │ │ ├── auth_middleware.dart
│ │ │ ├── cors_middleware.dart
│ │ │ └── logging_middleware.dart
│ │ ├── models/
│ │ │ └── user.dart
│ │ ├── repositories/
│ │ │ └── user_repository.dart
│ │ └── services/
│ │ └── user_service.dart
│ └── myapp.dart # Library barrel export
├── test/
│ ├── handlers/
│ │ └── users_handler_test.dart
│ └── middleware/
│ └── auth_middleware_test.dart
├── pubspec.yaml
├── analysis_options.yaml
└── Dockerfilebin/server.dartshelf_io.servelib/src/app.dartRouter get routerMiddlewareHandler Function(Handler)freezedjson_serializablemyapp/
├── bin/
│ └── server.dart # 入口文件(代码精简:仅做配置、启动、关停逻辑)
├── lib/
│ ├── src/
│ │ ├── app.dart # Pipeline + Router 组装逻辑
│ │ ├── config/
│ │ │ └── config.dart # 基于环境变量的配置
│ │ ├── handlers/
│ │ │ ├── health_handler.dart
│ │ │ └── users_handler.dart
│ │ ├── middleware/
│ │ │ ├── auth_middleware.dart
│ │ │ ├── cors_middleware.dart
│ │ │ └── logging_middleware.dart
│ │ ├── models/
│ │ │ └── user.dart
│ │ ├── repositories/
│ │ │ └── user_repository.dart
│ │ └── services/
│ │ └── user_service.dart
│ └── myapp.dart # 库的桶导出文件
├── test/
│ ├── handlers/
│ │ └── users_handler_test.dart
│ └── middleware/
│ └── auth_middleware_test.dart
├── pubspec.yaml
├── analysis_options.yaml
└── Dockerfilebin/server.dartshelf_io.servelib/src/app.dartRouter get routerMiddlewareHandler Function(Handler)freezedjson_serializabledependencies:
shelf: ^1.4.0
shelf_router: ^1.1.0
shelf_static: ^1.1.0 # Static file serving
shelf_web_socket: ^1.0.0 # WebSocket support
# Data
freezed_annotation: ^2.4.0
json_annotation: ^4.8.0
# Auth
dart_jsonwebtoken: ^2.12.0
bcrypt: ^1.1.0
dev_dependencies:
test: ^1.24.0
mocktail: ^1.0.0
build_runner: ^2.4.0
freezed: ^2.4.0
json_serializable: ^6.7.0dependencies:
shelf: ^1.4.0
shelf_router: ^1.1.0
shelf_static: ^1.1.0 # 静态文件服务
shelf_web_socket: ^1.0.0 # WebSocket支持
# 数据处理
freezed_annotation: ^2.4.0
json_annotation: ^4.8.0
# 认证
dart_jsonwebtoken: ^2.12.0
bcrypt: ^1.1.0
dev_dependencies:
test: ^1.24.0
mocktail: ^1.0.0
build_runner: ^2.4.0
freezed: ^2.4.0
json_serializable: ^6.7.0// bin/server.dart
import 'dart:io';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:myapp/myapp.dart';
Future<void> main() async {
final config = Config.fromEnvironment();
final app = Application(config);
final handler = await app.createHandler();
final server = await shelf_io.serve(
handler,
InternetAddress.anyIPv4,
config.port,
);
print('Server running on http://${server.address.host}:${server.port}');
// Graceful shutdown
ProcessSignal.sigint.watch().listen((_) async {
print('Shutting down...');
await app.close();
await server.close();
exit(0);
});
}// bin/server.dart
import 'dart:io';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:myapp/myapp.dart';
Future<void> main() async {
final config = Config.fromEnvironment();
final app = Application(config);
final handler = await app.createHandler();
final server = await shelf_io.serve(
handler,
InternetAddress.anyIPv4,
config.port,
);
print('Server running on http://${server.address.host}:${server.port}');
// 优雅关停
ProcessSignal.sigint.watch().listen((_) async {
print('Shutting down...');
await app.close();
await server.close();
exit(0);
});
}ProcessSignal.sigintapp.close()server.close()main()ProcessSignal.sigintserver.close()app.close()main()Configfactory Config.fromEnvironment()Platform.environmentStateErrorconstConfigfactory Config.fromEnvironment()Platform.environmentStateErrorconst// lib/src/app.dart
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
class Application {
final Config config;
late final UserRepository _userRepository;
late final UserService _userService;
Application(this.config);
Future<Handler> createHandler() async {
_userRepository = UserRepository();
_userService = UserService(_userRepository, config);
final healthHandler = HealthHandler();
final usersHandler = UsersHandler(_userService);
final router = Router()
..mount('/health', healthHandler.router.call)
..mount('/api/v1/users', usersHandler.router.call);
final pipeline = const Pipeline()
.addMiddleware(loggingMiddleware())
.addMiddleware(corsMiddleware())
.addMiddleware(handleErrors())
.addHandler(router.call);
return pipeline;
}
Future<void> close() async {
await _userRepository.close();
}
}// lib/src/app.dart
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
class Application {
final Config config;
late final UserRepository _userRepository;
late final UserService _userService;
Application(this.config);
Future<Handler> createHandler() async {
_userRepository = UserRepository();
_userService = UserService(_userRepository, config);
final healthHandler = HealthHandler();
final usersHandler = UsersHandler(_userService);
final router = Router()
..mount('/health', healthHandler.router.call)
..mount('/api/v1/users', usersHandler.router.call);
final pipeline = const Pipeline()
.addMiddleware(loggingMiddleware())
.addMiddleware(corsMiddleware())
.addMiddleware(handleErrors())
.addHandler(router.call);
return pipeline;
}
Future<void> close() async {
await _userRepository.close();
}
}.callRouteraddHandlerconst Pipeline()RouteraddHandler.callconst Pipeline()Router get routerRequestResponse// lib/src/handlers/users_handler.dart
class UsersHandler {
final UserService _userService;
UsersHandler(this._userService);
Router get router {
final router = Router();
// Public routes
router.post('/register', _register);
router.post('/login', _login);
// Protected routes
router.get('/', _withAuth(_getAll));
router.get('/<id>', _withAuth(_getById));
router.put('/<id>', _withAuth(_update));
router.delete('/<id>', _withAuth(_delete));
return router;
}
Handler _withAuth(Handler handler) {
return const Pipeline()
.addMiddleware(authMiddleware())
.addHandler(handler);
}
Future<Response> _register(Request request) async {
final body = await request.readAsString();
final json = jsonDecode(body) as Map<String, dynamic>;
final user = await _userService.register(
email: json['email'] as String,
password: json['password'] as String,
name: json['name'] as String,
);
return Response(
201,
body: jsonEncode(user.toJson()),
headers: {'Content-Type': 'application/json'},
);
}
}Router get routerRequestResponse// lib/src/handlers/users_handler.dart
class UsersHandler {
final UserService _userService;
UsersHandler(this._userService);
Router get router {
final router = Router();
// 公开路由
router.post('/register', _register);
router.post('/login', _login);
// 受保护路由
router.get('/', _withAuth(_getAll));
router.get('/<id>', _withAuth(_getById));
router.put('/<id>', _withAuth(_update));
router.delete('/<id>', _withAuth(_delete));
return router;
}
Handler _withAuth(Handler handler) {
return const Pipeline()
.addMiddleware(authMiddleware())
.addHandler(handler);
}
Future<Response> _register(Request request) async {
final body = await request.readAsString();
final json = jsonDecode(body) as Map<String, dynamic>;
final user = await _userService.register(
email: json['email'] as String,
password: json['password'] as String,
name: json['name'] as String,
);
return Response(
201,
body: jsonEncode(user.toJson()),
headers: {'Content-Type': 'application/json'},
);
}
}_withAuth(handler)request.readAsString()jsonDecode()Content-Type: application/json201204200shelf_router/<id>_withAuth(handler)request.readAsString()jsonDecode()Content-Type: application/json201204200shelf_router/<id>MiddlewareHandlerHandler// Middleware type signature
typedef Middleware = Handler Function(Handler innerHandler);MiddlewareHandlerHandler// Middleware类型签名
typedef Middleware = Handler Function(Handler innerHandler);Middleware loggingMiddleware() {
return (Handler innerHandler) {
return (Request request) async {
final stopwatch = Stopwatch()..start();
print('[${DateTime.now()}] ${request.method} ${request.requestedUri}');
final response = await innerHandler(request);
stopwatch.stop();
print(
'[${DateTime.now()}] ${request.method} ${request.requestedUri} '
'${response.statusCode} ${stopwatch.elapsedMilliseconds}ms',
);
return response;
};
};
}Middleware loggingMiddleware() {
return (Handler innerHandler) {
return (Request request) async {
final stopwatch = Stopwatch()..start();
print('[${DateTime.now()}] ${request.method} ${request.requestedUri}');
final response = await innerHandler(request);
stopwatch.stop();
print(
'[${DateTime.now()}] ${request.method} ${request.requestedUri} '
'${response.statusCode} ${stopwatch.elapsedMilliseconds}ms',
);
return response;
};
};
}Middleware handleErrors() {
return (Handler innerHandler) {
return (Request request) async {
try {
return await innerHandler(request);
} on NotFoundException catch (e) {
return _jsonError(404, e.message);
} on ValidationException catch (e) {
return _jsonError(422, e.message, errors: e.errors);
} on UnauthorizedException {
return _jsonError(403, 'Unauthorized');
} catch (e, stack) {
print('Error: $e\n$stack');
return _jsonError(500, 'Internal server error');
}
};
};
}
Response _jsonError(int status, String message, {Map<String, dynamic>? errors}) {
return Response(
status,
body: jsonEncode({
'error': message,
if (errors != null) 'errors': errors,
}),
headers: {'Content-Type': 'application/json'},
);
}Middleware handleErrors() {
return (Handler innerHandler) {
return (Request request) async {
try {
return await innerHandler(request);
} on NotFoundException catch (e) {
return _jsonError(404, e.message);
} on ValidationException catch (e) {
return _jsonError(422, e.message, errors: e.errors);
} on UnauthorizedException {
return _jsonError(403, 'Unauthorized');
} catch (e, stack) {
print('Error: $e\n$stack');
return _jsonError(500, 'Internal server error');
}
};
};
}
Response _jsonError(int status, String message, {Map<String, dynamic>? errors}) {
return Response(
status,
body: jsonEncode({
'error': message,
if (errors != null) 'errors': errors,
}),
headers: {'Content-Type': 'application/json'},
);
}MiddlewareHandlerawait innerHandler(request)request.change(context: {...request.context, 'key': value})shelf_io.serveMiddlewareHandlerawait innerHandler(request)request.change(context: {...request.context, 'key': value})shelf_io.serve| Source | How | Example |
|---|---|---|
| Body | | |
| Query params | | |
| Path params | Extra function arguments (shelf_router) | |
| Headers | | |
| Context | | |
| 来源 | 获取方式 | 示例 |
|---|---|---|
| 请求体 | | |
| 查询参数 | | |
| 路径参数 | 额外的函数参数(shelf_router提供) | |
| 请求头 | | |
| 上下文 | | |
| Status | Method |
|---|---|
| 200 OK | |
| 201 Created | |
| 204 No Content | |
| 404 Not Found | |
| Modify response | |
| 状态码 | 用法 |
|---|---|
| 200 OK | |
| 201 Created | |
| 204 No Content | |
| 404 Not Found | |
| 修改响应 | |
int.tryParsereadAsString()request.change(context:)Content-Typeint.tryParsereadAsString()request.change(context:)Content-Typefinal router = Router()
..get('/health', _health)
..get('/users', _listUsers)
..get('/users/<id>', _getUser)
..post('/users', _createUser)
..put('/users/<id>', _updateUser)
..delete('/users/<id>', _deleteUser);
// Mount sub-routers with path prefix
final root = Router()
..mount('/api/v1', apiRouter.call)
..mount('/ws', webSocketHandler);final router = Router()
..get('/health', _health)
..get('/users', _listUsers)
..get('/users/<id>', _getUser)
..post('/users', _createUser)
..put('/users/<id>', _updateUser)
..delete('/users/<id>', _deleteUser);
// 带路径前缀挂载子路由
final root = Router()
..mount('/api/v1', apiRouter.call)
..mount('/ws', webSocketHandler);..method('/path', handler)..mount('/prefix', router.call)/<id>/<slug>/api/v1/.....method('/path', handler)..mount('/prefix', router.call)/<id>/<slug>/api/v1/...Cascadeimport 'package:shelf/shelf.dart';
import 'package:shelf_static/shelf_static.dart';
final cascade = Cascade()
.add(apiRouter)
.add(createStaticHandler('public', defaultDocument: 'index.html'));
final handler = const Pipeline()
.addMiddleware(loggingMiddleware())
.addHandler(cascade.handler);Cascadeimport 'package:shelf/shelf.dart';
import 'package:shelf_static/shelf_static.dart';
final cascade = Cascade()
.add(apiRouter)
.add(createStaticHandler('public', defaultDocument: 'index.html'));
final handler = const Pipeline()
.addMiddleware(loggingMiddleware())
.addHandler(cascade.handler);CascadestatusCodesCascadestatusCodesclass UnauthorizedException implements Exception {
final String message;
UnauthorizedException([this.message = 'Unauthorized']);
}
class NotFoundException implements Exception {
final String message;
NotFoundException(this.message);
}
class ValidationException implements Exception {
final String message;
final Map<String, List<String>> errors;
ValidationException(this.message, [this.errors = const {}]);
}class UnauthorizedException implements Exception {
final String message;
UnauthorizedException([this.message = 'Unauthorized']);
}
class NotFoundException implements Exception {
final String message;
NotFoundException(this.message);
}
class ValidationException implements Exception {
final String message;
final Map<String, List<String>> errors;
ValidationException(this.message, [this.errors = const {}]);
}ExceptionErrorException('message')ExceptionErrorException('message')undefinedundefinedundefinedundefinedPipelinerequest.change(context:)shelfmocktailPipelinerequest.change(context:)shelfmocktail