Astro:如何代理服务调用



我正在设置一个Astro站点,该站点将显示从运行在同一主机但不同端口上的简单服务获取的数据。

该服务是一个简单的Express应用程序。server.js:

const express = require('express')
const app = express()
const port = 3010
const response = {
message: "hello"
}
app.get('/api/all', (_req, res) => {
res.send(JSON.stringify(response))
})
app.listen(port, () => {
console.log(`listening on port ${port}`)
})

由于服务运行在端口3010上,这与Astro站点不同,所以我在Vite级别配置了一个服务器代理。astro.config.mjs:

import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [react()],
vite: {
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
},
server: {
proxy: {
'/api/all': 'http://localhost:3010'
}
}
},
});

这是我试图调用服务的地方。index.astro:

---
const response = await fetch('/api/all');
const data = await response.json();
console.log(data);
---

当我运行yarn dev时,我得到这个控制台输出:

Response {
size: 0,
[Symbol(Body internals)]: {
body: Readable {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
stream: Readable {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
boundary: null,
disturbed: false,
error: null
},
[Symbol(Response internals)]: {
type: 'default',
url: undefined,
status: 404,
statusText: '',
headers: { date: 'Tue, 02 Aug 2022 19:41:02 GMT' },
counter: undefined,
highWaterMark: undefined
}
}

看起来网络请求正在返回一个404。

我在文档中没有看到更多关于服务器配置的内容。我这样做对吗?

我使用香草Vite应用程序和相同的配置/设置可以正常工作。

如何代理Astro应用程序的本地服务调用

简短回答

您不能使用Astro代理服务呼叫,但也不必使用

有关直接解决方案的答案,请参阅无代理的功能测试一节

详细信息

  • Astro不会将server.proxy配置转发给Vite(除非您修补了自己版本的Astro(,Astro Vite服务器配置可以看到为空
proxy: {
// add proxies here
},

参考https://github.com/withastro/astro/blob/8c100a6fe6cc652c3799d1622e12c2c969f30510/packages/astro/src/core/create-vite.ts#L125

  • Astro服务器与Astro vite.server配置合并,但不采用代理参数。这一点从代码中并不明显,请稍后参阅测试
let result = commonConfig;
result = vite.mergeConfig(result, settings.config.vite || {});
result = vite.mergeConfig(result, commandConfig);

参考https://github.com/withastro/astro/blob/8c100a6fe6cc652c3799d1622e12c2c969f30510/packages/astro/src/core/create-vite.ts#L167

测试

配置测试

我尝试了所有可能的组合,即如何将配置输入到Astro,并在每个位置使用不同的端口号来显示哪一个使用覆盖

  • 根目录上的vite.config.js文件
export default {
server: {
port:6000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
  • 在根文件astro.config.js的两个位置
    • 服务器
    • vite.server
export default defineConfig({
server:{
port: 3000,
proxy: {
'/api': 'http://localhost:4000'
}
},
integrations: [int_test()],
vite: {
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
},
server: {
port:5000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
});
Astro集成中的

Astro有一个所谓的集成,它有助于更新配置(Astro插件的一种(集成有助于识别最终保存在配置中的内容,也为更新配置提供了最后的机会

integration-test.js

async function config_setup({ updateConfig, config, addPageExtension, command }) {
green_log(`astro:config:setup> running (${command})`)
updateConfig({
server:{proxy : {'/api': 'http://localhost:4000'}},
vite:{server:{proxy : {'/api': 'http://localhost:4000'}}}
})
console.log(config.server)
console.log(config.vite)
green_log(`astro:config:setup> end`)
}

这是输出日志

astro:config:setup> running (dev)
{ host: false, port: 3000, streaming: true }
{
optimizeDeps: { esbuildOptions: { define: [Object] } },
server: { port: 5000, proxy: { '/api': 'http://localhost:4000' } }
}
astro:config:setup> end

代理参数从astro服务器配置中删除,vite配置可见,但由于被覆盖而无效,并且不会转发给vite

测试结果

  • dev服务器运行在端口3000上,该端口来自Astro配置server,所有其他配置都被覆盖
  • fetch api失败并出现错误
error   Failed to parse URL from /api
File:
D:devastroastro-examples24_api-proxyD:devastroastro-examples24_api-proxysrcpagesindex.astro:15:20
Stacktrace:
TypeError: Failed to parse URL from /api
at Object.fetch (node:internal/deps/undici/undici:11118:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

无代理功能测试

考虑到Astro前端事务在服务器端运行,在构建期间以SSG模式运行,在服务器上页面加载时以SSR模式运行,然后服务器发送结果html,Astro可以访问所有主机端口,并可以直接使用类似的服务端口

const response = await fetch('http://localhost:4000/api');
const data = await response.json();
console.log(data);

上面的代码按预期运行,没有错误

参考示例

上面提到的所有测试和文件都可以在参考示例github repo上获得:https://github.com/MicroWebStacks/astro-examples/tree/main/24_api-proxy

您可以使用astro:server:setup钩子添加自己的代理中间件。

例如,在服务器设置挂钩中使用http代理中间件。

// plugins/proxy-middleware.mjs
import { createProxyMiddleware } from "http-proxy-middleware"
export default (context, options) => {
const apiProxy = createProxyMiddleware(context, options)
return {
name: 'proxy',
hooks: {
'astro:server:setup': ({ server }) => {
server.middlewares.use(apiProxy)
}
}
}
}

用法:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import proxyMiddleware from './plugins/proxy-middleware.mjs';
// https://astro.build/config
export default defineConfig({
integrations: [
proxyMiddleware("/api/all", {
target: "http://localhost:3010",
changeOrigin: true,
}),
],
});

最新更新