整合Express与json-server时两类请求无法同时响应的问题求助
问题描述
- 开发中转API,接收客户端请求转发至外部API,通过
userData.json存储用户数据(含API密钥),包含外部API路由及/create-user路由(生成唯一API密钥并存入文件) - 本地开发阶段API运行正常,但部署至Vercel时,无服务器函数无法修改文件,因此尝试整合json-server解决存储问题
- 当前本地运行异常:用node启动API可正常转发外部请求,但json-server相关请求返回404;用json-server启动则相反,两类请求无法同时正常工作
当前代码示例
// Import Express.js import http from 'https' import express from 'express' import jsonServer from 'json-server' import path from 'path' import {authenticateKey, createUser} from './api/apiAuth.js' import {ixApi} from './ixApi.js' // This variable defines the port of your computer where the API will be available const PORT = 3000 // This variable instantiate the Express.js library const app = express() app.use(express.json()); app.use( express.urlencoded({ extended: true, }) ); // JSON Server module const server = jsonServer.create(); const router = jsonServer.router("db.json"); //const router = jsonServer.router(path.join("", 'db.json')) // Make sure to use the default middleware const middlewares = jsonServer.defaults(); server.use(middlewares); // Add this before server.use(router) server.use( /** * Welcome message */ app.get('/', (request, response) => { // The string we want to display on http://localhost:3000 response.send('Welcome on the ix interface API!') }), /** * @function create-user - Creates new user */ app.post('/create-user', authenticateKey, async (req, res) => { //create a new user with "user:Username" let username = req.body.username let appName = req.body.appName let userEmail = req.body.userEmail let IX_ACCOUNT_NAME = req.body.IX_ACCOUNT_NAME let IX_API_KEY = req.body.IX_API_KEY let user = await createUser(username, appName, userEmail, IX_ACCOUNT_NAME, IX_API_KEY, req, res) console.log('USER 1:') console.log(user) res.status(201).send({ data: user }); }), /** * @function createInvoice - Creates invoice * * CALLED BY: * - * * TO IMPROVE: * - */ app.post('/create-invoice', authenticateKey, async (request, response) => { console.log(response.locals.ixCredentials) console.log(request.body) console.log(request.body.invoiceData) const IX_ACCOUNT_NAME = response.locals.ixCredentials.IX_ACCOUNT_NAME const IX_API_KEY = response.locals.ixCredentials.IX_API_KEY const invoiceData = request.body.invoiceData const items = request.body.invoiceData.items const creationRequest = () => { const response = new Promise ((resolve) => { var options = { "method": "POST", "hostname": IX_ACCOUNT_NAME + ".app.ix.com", "port": null, "path": "/invoices.json?api_key=" + IX_API_KEY, "headers": { "accept": "application/json", "content-type": "application/json" } }; var req = http.request(options, function (res) { var chunks = []; res.on("data", function (chunk) { console.log('CHUNK') console.log(chunk.toString) chunks.push(chunk); }); res.on("end", function () { var body = Buffer.concat(chunks); console.log('IX BODY:') console.log(body.toString()); resolve(body.toString()) }); console.log(res.statusCode) }) var itemsList = [] for (let i = 0; i < items.length; i++) { itemsList.push({ name: items[i].name, description: items[i].description, unit_price: items[i].price, quantity: items[i].quantity, }) console.log(itemsList) } req.write(JSON.stringify({ invoice: { date: invoiceData.date, due_date: invoiceData.due_date, client: { name: invoiceData.client.name, email: invoiceData.client.email, country: invoiceData.client.country, fiscal_id: invoiceData.client.vat, code: invoiceData.client.vat, }, items: itemsList, }})); req.on('error', function(err) { console.log('IX ERROR:') console.log(err) //return response.json({ success: false }) }); console.log('SENDING REQUEST:') req.end(); }) console.log('RESPONSE') console.log(response) return response } const returnedData = await creationRequest() .then (response => { console.log('INVOICE CREATED') var obj = JSON.parse(response) console.log(obj) return obj }); console.log('RETURNED DATA') console.log(returnedData) return response.json(returnedData) }) ) server.use( jsonServer.rewriter({ "/api/*": "/$1", }) ) server.use(router); /** * The code below starts the API with these parameters: * 1 - The PORT where your API will be available * 2 - The callback function (function to call) when your API is ready */ server.listen(PORT, () => console.log(`The ix API is running on: http://localhost:${PORT}.`) ) // Export the Server API export {server}
解决方案
问题核心是你创建了两个独立的Express实例(app和server),并错误地将app的路由挂载到server上,导致路由逻辑冲突。正确的做法是将json-server作为Express的中间件,复用同一个服务器实例。
修改后的代码如下:
// Import dependencies import http from 'https' import jsonServer from 'json-server' import path from 'path' import {authenticateKey, createUser} from './api/apiAuth.js' import {ixApi} from './ixApi.js' const PORT = 3000 // 直接使用jsonServer创建Express实例,无需单独创建express() const server = jsonServer.create(); const router = jsonServer.router("db.json"); const middlewares = jsonServer.defaults(); // 挂载请求解析中间件(json-server已封装bodyParser) server.use(jsonServer.bodyParser()); // 如需自定义urlencoded配置,可单独添加: // server.use(express.urlencoded({ extended: true })); // 加载json-server默认中间件(静态文件、CORS等) server.use(middlewares); // 挂载自定义路由 server.get('/', (request, response) => { response.send('Welcome on the ix interface API!') }) server.post('/create-user', authenticateKey, async (req, res) => { let username = req.body.username let appName = req.body.appName let userEmail = req.body.userEmail let IX_ACCOUNT_NAME = req.body.IX_ACCOUNT_NAME let IX_API_KEY = req.body.IX_API_KEY let user = await createUser(username, appName, userEmail, IX_ACCOUNT_NAME, IX_API_KEY, req, res) console.log('USER 1:') console.log(user) res.status(201).send({ data: user }); }) server.post('/create-invoice', authenticateKey, async (request, response) => { console.log(response.locals.ixCredentials) console.log(request.body) console.log(request.body.invoiceData) const IX_ACCOUNT_NAME = response.locals.ixCredentials.IX_ACCOUNT_NAME const IX_API_KEY = response.locals.ixCredentials.IX_API_KEY const invoiceData = request.body.invoiceData const items = request.body.invoiceData.items const creationRequest = () => { // 修复变量名冲突:原Promise内部使用了response变量,与外部参数重名 return new Promise ((resolve) => { var options = { "method": "POST", "hostname": IX_ACCOUNT_NAME + ".app.ix.com", "port": null, "path": "/invoices.json?api_key=" + IX_API_KEY, "headers": { "accept": "application/json", "content-type": "application/json" } }; var req = http.request(options, function (res) { var chunks = []; res.on("data", function (chunk) { console.log('CHUNK') console.log(chunk.toString) chunks.push(chunk); }); res.on("end", function () { var body = Buffer.concat(chunks); console.log('IX BODY:') console.log(body.toString()); resolve(body.toString()) }); console.log(res.statusCode) }) var itemsList = [] for (let i = 0; i < items.length; i++) { itemsList.push({ name: items[i].name, description: items[i].description, unit_price: items[i].price, quantity: items[i].quantity, }) } req.write(JSON.stringify({ invoice: { date: invoiceData.date, due_date: invoiceData.due_date, client: { name: invoiceData.client.name, email: invoiceData.client.email, country: invoiceData.client.country, fiscal_id: invoiceData.client.vat, code: invoiceData.client.vat, }, items: itemsList, }})); req.on('error', function(err) { console.log('IX ERROR:') console.log(err) }); req.end(); }) } const returnedData = await creationRequest() .then (data => { console.log('INVOICE CREATED') return JSON.parse(data) }); console.log('RETURNED DATA') console.log(returnedData) return response.json(returnedData) }) // 给json-server路由加前缀,避免与自定义路由冲突 server.use('/api', router); // 启动服务器 server.listen(PORT, () => console.log(`The ix API is running on: http://localhost:${PORT}.`) ) export {server}
关键修改说明
- 移除独立的
express()实例,直接使用jsonServer.create()返回的Express实例,确保路由和中间件都在同一个服务器上运行 - 使用
jsonServer.bodyParser()替代原有的express.json()和express.urlencoded(),简化中间件配置 - 将自定义路由直接挂载到
server实例上,而非错误地传入server.use()的参数列表 - 给json-server的路由添加
/api前缀,避免与自定义路由(如/create-user)冲突,可根据需求调整前缀 - 修复了
creationRequest函数中的变量名冲突问题,避免逻辑异常
内容的提问来源于stack exchange,提问作者jjm
相关产品推荐
相关产品推荐

