qwik, post 的 body 数据丢失

2023-06-21

背景

本机 ubuntu 20,node 20, qwik 1.1.5, fastify 4.18.0

使用 qwik 通过 trpc http link 的方式调用暴露出来的 qwik 的 onRequest 的 trpc 端点,调用是正常的。但是在服务器上,服务无法拿到 request 中的 body 数据。

初次探索

node_modules/@trpc/server/dist/adapters/fetch/index.mjs 这个模块负责对 把 fastify 的请求 转成 qwik 的请求。

body: opts.req.headers.get('content-type') === 'application/json' ? await opts.req.text() : ''

语句中,

  • 对 opts.req 运行 opts.req.json() 返回无法解析。
  • 对 opts.req 运行 opts.req.text() 返回 body is unusable。

网上给的 readablestream 的读入方式太麻烦了。

  • 尝试:现在猜测是请求发出的时候,在 caddy 中转或者在某中间链路中发生丢失导致的问题。本机调用的时候给的 TRPC endpoint 是自己本机的端口地址,而服务器上的是给的是域名地址。把服务器上的域名地址改到本机端口地址,结果问题依然发生。所以不是这个问题导致的。
  • 尝试:node 版本和依赖完全统一,依然发生问题,不是由于依赖或者运行环境引起的。
内部调用的解决办法

将 http 调用改为过程调用。避开了问题,问题解决。

用 SPA 应用调用,问题依然发生,由于还是要对外进行服务的,需要找真正原因。

查找过程

TRPC 给的适配器是 fetch 原生,不包括 qwik。qwik 传入的 request 是跟平台无关的,导致这个适配问题的产生。服务跑的是传统的 fastify, https://github.com/fastify/fastify。

如果你的 qwik 是经过 fastify 包装的, 入口是 entry.fastify.tsx。

await fastify.register(FastifyQwik, { distDir, buildDir });

fastify 会把进来的请求进行一次包装,在 plugin 那一层放出给到 qwik 应用。 fastify 不处理的流量给到 qwik 的 router 里。

const { router, notFound } = createQwikCity({ render, qwikCityPlan });

fastify.setNotFoundHandler(async (request, response) => {
    // 在这个部分 fastify 的 post 数据是存在的。
    await router(request.raw, response.raw, fastify.log.error);
    await notFound(request.raw, response.raw, fastify.log.error);
});

qwik 会去拿 request.raw 原始的 request 类型 - node-incomingmessage

node_modules/@builder.io/qwik-city/middleware/node/index.d.ts

const serverRequestEv = await fromNodeHttp(getUrl(req, opts.origin), req, res, 'server');

实现跨平台的方式,它使用适配器模式,在 fromNodeHttp 他会进行重新包装自有 request。


  const getRequestBody = async function* () {
    for await (const chunk of req) {
      yield chunk;
    }
  };
  const body = req.method === 'HEAD' || req.method === 'GET' ? void 0 : getRequestBody();
  const options = {
    method: req.method,
    headers: requestHeaders,
    body,
    duplex: 'half',
  };

至此,判断可能是某个环节中转导致问题发生。大概率是 fastify 的 request.raw 中转导致的。

备注

qwik 会把里面的内容一并打包到 entry.fastify.js

import { createQwikCity } from '@builder.io/qwik-city/middleware/node';

pipe 越少,问题越少

原本在迁移过程前, 想把 trpc 转移至 qwik 外的 fastify, 看 qwik 本身有处理 plugin 的能力,就把 trpc 的 endpoint 放在了 qwik router 里面了。

但是 qwik 的文档里面 deploy 都是需要适配器使用的,无法单独部署。不可直接使用 vite preview 作为服务。

https://github.dev/wmalarski/qwik-trpc-supabase/blob/master/src/entry.dev.tsx 基于 trpc 的适配方式,但这个没有选择前置服务器,vite preview 只作为预览是可行的

解决方案:

  • fastify 提前接管 trpc 流量,不走 qwik 流量。

总结

  • 预览的效果和最终效果一致能减少很多不必要的工作。
  • 「若無必要,勿增實體」(Non sunt multiplicanda entia sine necessitate)
copyright ©2019-2024 shenzhen
粤ICP备20041170号-1