stonexer / yab

🐈 A fetch library
4 stars 0 forks source link

RFC: middleware #5

Open stonexer opened 5 years ago

stonexer commented 5 years ago

看了 @leohxj 总结的各大请求库的 interceptor | middleware | hook,一般用于请求返回值的处理,日志和错误处理等场景。在这里先总结一下:

Axios

使用 interceptor, transform

interceptor 可以设置在 global 和实例上,采用链式调用的顺序依次执行和传递上一个返回的参数。 transformRequest 和 transformResponse 位于最接近 request 的位置,只能处理 data(POST,PUT,...) 和 header。

感觉有点乱,可能有历史包袱

Before

interceptors.request.use 可以处理和修改请求 config。

transformRequest 可以处理和修改 data,header。

After

interceptors.response.use可以处理和修改 response。

transformRequest 可以做 data,header 的处理和修改

旧的 Hooks 想法

隐含逻辑太多,已放弃

fetch.useMiddleware(
  (yabRequestInit: YabRequestInit) => (pResult: Promise<PrevResult>) => Promise<NextResult>
);

Example

// Resolve json from resposne
fetch.useMiddleware(
  () => (pResult) => pResult.then((response) => response.json())
);

// Resolve data from json
fetch.useMiddleware(
  () => (pJSON) => pJSON.then((json) => json.data)
);

// Cache Layer
fetch.useMiddleware(
  (init) => {
    if (hasCache(init)) {
      // TODECIDE: Use init.skipResult as escape hatch ?
      init.skipResult = Promise.resolve(getCache(init));
    }

    return (pData) => pData.then((data) => {
      setCache(getCacheKey(init), data);
      return data;
    });
  }
);

leohxj commented 5 years ago

middleware 的通用参数是哪些, init 是什么含义?

没太理解你这里面打算怎么把 middleware 串起来执行的,是把 fetch 生成的对象依次入参给 middleware, 然后把后续的 middleware 返回作为下一个入参么? 第三个最后还要 return data 吧。

stonexer commented 5 years ago
  1. 含义见修改的 API

请求发送之前,依次通过 middware 处理加工请求的 requestInit,middleware 的返回值是请求 response 的 handler,请求返回后再依次通过这些 handler。放在同一个闭包里的好处是 response 的 handler 可以直接访问到 requestInit ... 虽然不用闭包也能办到。。。

  1. 大哥你看的太认真了,已修改
leohxj commented 5 years ago

借鉴 Apollo Link, 引入 Afterware 概念:对响应的数据做一些业务处理。

Afterware 中,有一个常见的操作,就是数据转换,将原始的 response 处理为业务需要的 data.这部分可以抽象为 dataResolver 概念,由使用者单独定义。

leohxj commented 5 years ago

恩,我们先列举一下需要处理哪些场景。然后再定:


假设我们对 Request 做处理的场景是:

Response 的处理:

然后,我们再定这个处理函数的入参、出参。我在这个分支了写过一个: packages/yab-fetch/docs/interceptor.md

leohxj commented 5 years ago

场景一:log

能打印以下这些以及相对时间:

请求前的 yabRequestInit 完整的 Response 请求成功时的 data 请求失败时的 error

中间件采取 middleware 方式的代码示例

const log = (next) => (yabRequest) => {
  const startTime = new Date().getTime();
  console.log('yabRequest:', yabRequest);

  return  next(yabRequest).then(response => {
    console.log('raw response:', response);
    timeCost(startTime);
    // check status
    if (response.ok) {
      return response.json()
    } else {
      throw response;
    }
  }).then(data => {
    console.log('data:', data);
    timeCost(startTime);
  }).catch(e => {
    console.error('error:', e);
    timeCost(startTime);
  });
}

const timeCost = (startTime) => {console.log('time cost:', new Date().getTime() - startTime)};

applyMiddleware(...middleware)(innerFetch)(yabRequest);

innerFetch(yabRequest) {
  const { url, ...init } = yabRequest;

  return fetch(url, init);
}
stonexer commented 5 years ago

线下讨论之后,一致通过使用类似 Koa 的 middleware API。整理如下:

Koa-like API

fetch.use(async (context, next) => {
  const startTime = new Date();
  await next();
  console.info(new Date() - startTime);
});

Context

Request

ctx.getYabRequestInit();
ctx.setYabRequestInit();
// ...

Response

// Raw response
ctx.getResponse(); // response.clone()
ctx.setResponse();

// Consumer
ctx.json();
ctx.text();
ctx.getReader();

// ...

Default Middlewares

"?" means maybe "!" means should