Skip to content
This repository has been archived by the owner on Jul 29, 2019. It is now read-only.

RFC: middleware #5

Open
stonexer opened this issue Jun 28, 2019 · 6 comments
Open

RFC: middleware #5

stonexer opened this issue Jun 28, 2019 · 6 comments

Comments

@stonexer
Copy link
Owner

stonexer commented Jun 28, 2019

看了 @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。

  • 因为 axios 不基于 fetch,所以返回的 response 类型为 axios 自定义的 Response,不存在 fetch 的 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

This comment has been minimized.

@stonexer

This comment has been minimized.

@leohxj
Copy link
Collaborator

leohxj commented Jul 2, 2019

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

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

@leohxj
Copy link
Collaborator

leohxj commented Jul 10, 2019

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

  • 有哪几个处理的时机(request/ response/ error)?
  • 每个环节处理的入参和出参是什么

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

  • 依据 data 格式,字段添加 content-type
  • Authorization header

Response 的处理:

  • http code 业务状态判断,(也可内置)
  • 数据格式转换
  • 缓存

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

@leohxj
Copy link
Collaborator

leohxj commented Jul 11, 2019

场景一: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
Copy link
Owner Author

stonexer commented Jul 15, 2019

线下讨论之后,一致通过使用类似 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

  • yabRequestInit -> requestInit
  • ! Fetch
  • ? JSON handler when dataType==='json'
  • ? Default Error handler

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants