运行 Finch 应用程序

一个 Finch 应用程序需要两个文件:app.dart(应用程序设置)和一个入口点——通常是生产环境使用的 bin/main.dart 或开发时使用文件监视的 lib/serve.dart

app.dart — 应用程序设置

app.dart 创建 FinchApp 实例、注册路由并定义 main()。这是基于 Finch 示例项目的标准结构:

// lib/app.dart
import 'package:finch/finch_app.dart';
import 'package:finch/finch_tools.dart';
import 'package:finch/finch_route.dart';
import 'package:finch/finch_console.dart';
import 'route/web_route.dart';

FinchConfigs configs = FinchConfigs(
  port: env.getInt('DOMAIN_PORT', 8080),
  domain: env.get('DOMAIN', 'localhost'),
  publicDir: pathTo(env.get('PUBLIC_DIR', './public')),
  widgetsPath: pathTo(env.get('WIDGETS_PATH', './lib/widgets')),
  widgetsType: env.get('WIDGETS_TYPE', 'j2.html'),
  languagePath: pathTo(env.get('LANGUAGE_PATH', './lib/languages')),
  enableLocalDebugger: env.getBool('ENABLE_LOCAL_DEBUGGER', false),
);

final app = FinchApp(configs: configs);

void main([List<String>? args]) async {
  app.addRouting(getWebRoute);

  app.start(args).then((value) {
    Console.p('Server started: http://localhost:${value.port}');
  });
}

路由函数

路由在一个接收 Request 并返回 Future<List<FinchRoute>> 的函数中定义:

// lib/route/web_route.dart
import 'package:finch/finch_route.dart';
import '../controllers/home_controller.dart';

final homeController = HomeController();

Future<List<FinchRoute>> getWebRoute() async {
  return [
    FinchRoute(
      key: 'root',
      path: '/',
      methods: Methods.ONLY_GET,
      index: homeController.index,
    ),
    FinchRoute(
      key: 'root.api',
      path: 'api/hello',
      methods: Methods.ONLY_GET,
      index: homeController.hello,
    ),
  ];
}

内联路由(简写)

对于简单或快速的路由,FinchApp 提供了简写方法。index 回调接收 Request 作为参数:

app
  ..get(
    path: '/ping',
    index: (rq) async => rq.renderString(text: 'pong'),
  )
  ..post(
    path: '/echo',
    index: (rq) async {
      var body = rq.get<String>('message', def: '');
      return rq.renderString(text: body);
    },
  )
  ..postGet(
    path: '/form',
    index: (rq) async => rq.renderString(text: 'GET or POST'),
  );

开发:使用文件监视器的 serve.dart

开发时使用 lib/serve.dart(由 finch create 生成)。它运行 app.dart 中的 main() 并监视 widget 和语言文件的变更,自动重新生成 Dart source map 而无需完全重启服务器:

finch serve -p lib/serve.dart
# 或
dart run lib/serve.dart

监视器会在文件发生变更时调用 LanguageToDartWidgetToDart 转换器,然后通知已连接的调试栏重新加载页面。

生产环境:编译为二进制

编译为独立的本地二进制文件:

finch build -a lib/app.dart -o ./build/app
./build/app

或使用提供的 Dockerfile / docker-compose。参见 Docker

Cron 作业

使用 app.registerCron() 注册定时任务:

app.registerCron(
  FinchCron(
    schedule: FinchCron.evryDay(2),   // 每 2 天
    onCron: (index, cron) async {
      // 执行清理任务
    },
    delayFirstMoment: true,
  ).start(),
);

// 标准 cron 表达式
app.registerCron(
  FinchCron(
    schedule: '0 * * * *',  // 每小时
    onCron: (index, cron) async {
      // 执行每小时任务
    },
  ).start(),
);

处理已记录的错误

注册一个 app.onError() 回调,在每次通过 Console(例如 Console.e(...))记录错误时收到通知,例如可以将错误转发给外部监控服务:

app.onError((error, type) {
  // 'type' 是日志级别,例如 'error'
  MyMonitoringService.report(error, type);
});

传递参数

始终将 argsmain 传递给 app.start()。这使得 migrate 等 CLI 命令和自定义命令能够正常工作:

void main([List<String>? args]) async {
  app.addRouting(getWebRoute);
  app.start(args);
}
dart run lib/app.dart migrate --init