我正在设置一个 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。
我在doc中没有看到更多有关服务器配置的信息。 我以正确的方式处理这件事吗?
我可以在普通 Vite 应用程序和相同的配置/设置下正常工作。
如何代理 Astro 应用程序的本地服务调用?
astro:server:setup
钩子添加自己的代理中间件。
例如在服务器设置挂钩中使用 http-proxy-middleware。
// 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,
}),
],
});
您无法使用 Astro 代理服务调用,但您也不必这样做
有关直接解析答案,请参阅无需代理的功能测试部分
proxy: {
// add proxies here
},
let result = commonConfig;
result = vite.mergeConfig(result, settings.config.vite || {});
result = vite.mergeConfig(result, commandConfig);
我尝试了如何将配置输入 Astro 的所有可能组合,并在每个位置使用不同的端口号来显示哪个端口被覆盖
export default {
server: {
port:6000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
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 插件),该集成有助于识别最终保留在配置中的内容,并且还提供了更新配置的最后机会
集成测试.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
server
所有其他配置都被覆盖 error Failed to parse URL from /api
File:
D:\dev\astro\astro-examples\24_api-proxy\D:\dev\astro\astro-examples\24_api-proxy\src\pages\index.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 front Matter 运行在服务器端,构建期间处于 SSG 模式,服务器上页面加载时处于 SSR 模式,然后服务器发送结果 html,Astro 可以访问所有主机端口,并且可以直接使用服务端口,例如这个
const response = await fetch('http://localhost:4000/api');
const data = await response.json();
console.log(data);
上面的代码按预期运行,没有错误
上述所有测试和文件都可以在参考示例 github 存储库中找到:https://github.com/MicroWebStacks/astro-examples/tree/main/24_api-proxy
nwellis 的答案非常接近,但仅适用于开发服务器。如果你在生产中,那是行不通的。
这是一种在生产中可行的方法,但有一个警告。它需要 SSR 模式。您可以在here阅读有关动态路由的更多信息。另外,这仅限于节点版本 18 或更高版本,因为我在服务器上使用 fetch。如果您还在使用 Node 版本 < 18, fetch can be swapped with axios.
步骤1.
将 astro 配置输出值更改为“服务器”以允许 api 端点使用。
import { defineConfig } from "astro/config";
// https://astro.build/config
export default defineConfig({
output: "server",
// rest of config
});
第2步:
使用扩展语法创建一个名为
[...proxy].ts
的文件,位于 src > Pages > api > [...proxy].ts
这会动态匹配
api/*
处的任何 url。无论路径如何嵌套,它都将适用。
第三步:
添加以下内容或类似内容。这将捕获所有请求并转发到您想要的任何地方。
import type { APIRoute } from "astro";
const getProxyUrl = (request: Request) => {
const proxyUrl = new URL("http://localhost:3000");
const requestUrl = new URL(request.url);
return new URL(requestUrl.pathname, proxyUrl);
};
export const ALL: APIRoute = async ({ request }) => {
const proxyUrl = getProxyUrl(request);
const response = await fetch(proxyUrl.href, request);
return new Response(response.body);
};