Effects

效果是整个框架的主要组成部分。 它只是一个返回事件流的函数。 使用其通用接口,我们可以定义API端点,事件处理程序或中间件。

HttpEffect它的职责是将每个传入请求映射到响应对象。

美元符号:sometimes used by convention to indicate that a variable holds an Observable or that a function will return an Observable.

中间件

定义中间件:

import { HttpMiddlewareEffect } from '@marblejs/core';
import { tap } from 'rxjs/operators';
  
export const logger$: HttpMiddlewareEffect = (req$, res) =>
  req$.pipe(
    tap(req => console.log(`hhh ${req.method} ${req.url}`)),
  );  

导入使用:

import { httpListener } from '@marblejs/core';
import { logger$ } from './middleware/logger'
import { bodyParser$ } from '@marblejs/middleware-body';
import { api$ } from './api.effects';

const middlewares = [
  logger$,
  bodyParser$(),
];

const effects = [
  api$,
];

export const listener = httpListener({
  middlewares,
  effects,
});

这样每进来一个请求就会打印出method和url。

带参数的中间件

interface LoggerOpts {
  showUrl?: boolean;
}

export const logger$ = (opts: LoggerOpts = {}): HttpMiddlewareEffect => req$ =>
  req$.pipe(
    tap(req => console.log(`${req.method} ${opts.showUrl ? req.url : ''}`)),
  );

使用的时候带上参数:

const middlewares = [
  logger$({showUrl: false}),
];

这样就不会打印出url。

提前响应

给req提供了一个req.response.send来提前响应http,官网上写的req.res,测试发现没有res这个字段。

import { HttpMiddlewareEffect } from '@marblejs/core';
import { mergeMap } from 'rxjs/operators';
  
export const earlier$: HttpMiddlewareEffect = req$ =>
  req$.pipe(
    mergeMap(req => req.response.send({body: "earlier response!", status: 200})),
  );
const middlewares = [
  earlier$,
  logger$({showUrl: false}),
  bodyParser$(),
];

如果被提前返回了响应,那接下来的中间件都不会执行了。

在 API Effects 中使用中间件

比如把之前的earlier$从middlewares数组中删掉,然后在api.effects中引用。

import { r } from '@marblejs/core';
import { mapTo } from 'rxjs/operators';
import { earlier$ } from "./middleware/earlier"

export const api$ = r.pipe(
  r.matchPath('/'),
  r.matchType('GET'),
  r.use(earlier$),
  r.useEffect(req$ => req$.pipe(
     mapTo({ body: 'Hello, world!' }),
  )));

这个时候,同样会返回earlier$中的内容,而不是接下来的Hello, world!

路由

api.effects.ts定义成这样:

import { r, combineRoutes } from "@marblejs/core";
import { mapTo } from "rxjs/operators";
// import { earlier$ } from "./middleware/earlier"

const getRoot$ = r.pipe(
  r.matchPath("/"),
  r.matchType("GET"),
  r.useEffect(req$ => req$.pipe(mapTo({ body: "Hello, world!" })))
);

const getUser$ = r.pipe(
   r.matchPath("/user"),
   r.matchType("GET"),
   r.useEffect(req$ => req$.pipe(mapTo({ body: "get user" })))
 );

export const api$ = combineRoutes("/", [getRoot$, getUser$]);

通过combineRoutes来组合Effects ,这样就能响应/和/user了。combineRoutes表示当前的根路径,后面跟的Effects中匹配的都是该路径下的子路径。

带参数的url

增加一个Effects如下:参数都被放在req.params下。

const getUserInfo$ = r.pipe(
   r.matchPath("/user/:username"),
   r.matchType("GET"),
   r.useEffect(req$ => req$.pipe(
      tap(req => console.log("req.params === ", req.params)),
      pluck('params', 'username'),
      map(username => ({ body: `get user ${username}` }))))
 );

export const api$ = combineRoutes("/", [getRoot$, getUser$, getUserInfo$]);

根据不同的username返回不同的内容。

查询参数

修改getUser$

const getUser$ = r.pipe(
  r.matchPath("/user"),
  r.matchType("GET"),
  r.useEffect(req$ => req$.pipe(
     tap(req => console.log(req.query)),
     mapTo({ body: "get user" })))
);

请求:/user?name=kobe

解析得到:{ name: 'kobe' }