一个简单的 server 框架。
- 显式
declare()定义模块发现 - Koa-like 管线
- 基于文件系统的路由(不推荐)
- Service 依赖注入
- 可插拔日志
v4.x 只支持 ES module。
npm i --save @epiijs/server(root)
├─ src
│ ├─ handlers
│ │ ├─ users
│ │ │ └─ index.ts
│ │ └─ index.ts
│ └─ services
│ └─ userService
│ └─ index.ts
└─ start.ts对应的路由结果
=> /users (declare routes)
=> / (filesystem fallback)
startServer 接收加载解析的完整 IAppConfig。
import { getDirNameByImportMeta, importConfig } from '@epiijs/config';
import { startServer } from '@epiijs/server';
const config = await importConfig({
appRoot: getDirNameByImportMeta(import.meta)
});
startServer(config);约定在 /handlers 目录下提供请求处理器。推荐用 declare 显式注册路由:
import {
HandlerDeclareResult,
HandlerResult,
IncomingMessageWithParams,
IServiceLocator
} from '@epiijs/server';
export function declare(): HandlerDeclareResult {
return {
routes: [
{ method: 'GET', path: '/users' },
{ method: 'GET', path: '/users/:id' }
]
};
}
export default async function (
this: IServiceLocator,
message: IncomingMessageWithParams
): Promise<HandlerResult> {
const { method, params } = message;
// 简单响应
return 'hello world';
// 自定义响应
return {
status: 400,
headers: { 'content-type': 'application/json' },
content: JSON.stringify({})
};
}在 declare() 里用 handlers 组合 Handler 链(Koa-like 洋葱模型):
import {
HandlerDeclareResult,
HandlerFn,
HandlerResult,
IncomingMessageWithParams,
IServiceLocator
} from '@epiijs/server';
async function withTiming(
this: IServiceLocator,
message: IncomingMessageWithParams,
next: () => Promise<HandlerResult>
): Promise<HandlerResult> {
const start = Date.now();
const result = await next();
console.log('elapsed', Date.now() - start);
return result;
}
export function declare(): HandlerDeclareResult {
return {
routes: [{ method: 'GET', path: '/hello' }],
handlers: [withTiming]
};
}
export default async function (
this: IServiceLocator,
message: IncomingMessageWithParams
): Promise<HandlerResult> {
return 'hello world';
}在 /services 下提供 service 工厂。
export interface IUserService {
findUsers: () => Promise<IUser[]>;
}
export default function (services: IServiceLocator): IUserService {
return {
findUsers: async () => []
};
}Handler 内通过 this(绑定为 IServiceLocator)取用 service:
import {
HandlerResult,
IncomingMessageWithParams,
IServiceLocator
} from '@epiijs/server';
export default async function (
this: IServiceLocator,
message: IncomingMessageWithParams
): Promise<HandlerResult> {
const userService = this.userService as IUserService;
const users = await userService.findUsers();
return users;
}import { setTransport } from '@epiijs/server';
setTransport((method, ...args) => {
// method = 'info' | 'error' | 'warn' | 'debug' | 'log' | ...
// 转写给业务自己的日志器
});Handler 内通过 this.appLogger 取用日志器:
export default async function (
this: IServiceLocator,
message: IncomingMessageWithParams
): Promise<HandlerResult> {
this.appLogger.info('processing request', message.url);
return 'ok';
}actions/ → handlers/
// V3
export default async function (props: IncomingMessage, context: Context): Promise<ActionResult> {
const userService = await context.useService('UserService');
const config = context.getAppConfig();
// ...
}
// V4
export default async function (
this: IServiceLocator,
message: IncomingMessageWithParams
): Promise<HandlerResult> {
const userService = this.userService as IUserService;
const config = this.appConfig as IAppConfig;
// ...
}// V3
await context.useHandler(dispose => {
const start = Date.now();
if (message.method !== 'GET') {
return { status: 405, content: 'method not allowed' };
}
dispose(() => console.log('elapsed', Date.now() - start));
});
// V4:声明 handlers
async function withTiming(this: IServiceLocator, message: IncomingMessageWithParams, next: () => Promise<HandlerResult>) {
const start = Date.now();
const result = await next();
console.log('elapsed', Date.now() - start);
return result;
}
export function declare() {
return {
routes: [{ method: 'GET', path: '/hello' }],
handlers: [withTiming]
};
}| V3 | V4 |
|---|---|
ActionResult |
HandlerResult |
ActionFn |
HandlerFn |
ActionDeclareResult |
HandlerDeclareResult |
Context |
移除 |