Node.js端点预检响应缺失Access-Control-Allow-Origin导致CORS错误排查问询
核心问题点
- GCP API网关OPTIONS请求配置错误:你给
/shippingRates的OPTIONS方法绑定了auth0_jwt鉴权规则,但CORS预检请求本质是OPTIONS请求,不会携带Authorization请求头,会直接被网关鉴权拦截,根本无法转发到后端Node.js云函数,自然响应头里没有任何CORS相关配置,触发你看到的预检失败报错。 - OpenAPI配置语法错误:配置文件里的
path字段应该是复数形式paths,拼写错误会导致路由规则不生效,部分接口无法匹配到预设的CORS和转发规则。 - Node.js云函数逻辑缺陷:你把
connection.end()写在了异步数据库查询逻辑的外层,会出现数据库查询还没完成、响应还没返回,连接就被提前关闭的情况,导致CORS响应头无法正常写入返回值。
修复步骤
1. 修改GCP API网关的endpoint.yaml配置
swagger: '2.0' host: {gateway url here} x-google-endpoints: - name: {gateway url here} allowCors: True securityDefinitions: auth0_jwt: authorizationUrl: {auth0 url}/authorize flow: implicit type: oauth2 x-google-issuer: {auth0 url} x-google-jwks_uri: {auth0 url}/.well-known/jwks.json x-google-audiences: {auth0 url}/api/v2/ schemes: - https produces: - application/json # 修正为复数paths paths: /shippingRates: options: summary: handleoptions for shippingRates operationId: handleoptionsshippingRates x-google-backend: address: {Cloud Function Trigger URL} # 删除OPTIONS的鉴权配置 responses: '204': description: CORS preflight success post: summary: create a shipping rate operationId: createShippingRate x-google-backend: address: {Cloud Function Trigger URL} security: - auth0_jwt: [] responses: '200': description: A successful response schema: type: string # 其余get/delete/patch配置保持不变
如果不需要自定义OPTIONS逻辑,也可以直接删除OPTIONS的接口配置,网关开启allowCors: True后会自动处理预检请求,不需要转发到后端。
2. 修复Node.js云函数逻辑
const exportFunction = async (req, res) => { // 统一设置CORS头,避免重复配置 res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization'); res.header('Access-Control-Max-Age', '3600'); if (req.method === 'OPTIONS') { return res.status(204).send(''); } const connection = await makeConnection(); connection.connect(); const shippingRateId = req.query.shippingRateId; const queryCallback = (error, response) => { if(error) { res.status(400).send(error); } else { res.status(200).send(response); } // 把关闭连接的逻辑移到回调内部,确保请求处理完成再关闭 connection.end(); }; if (shippingRateId !== undefined) { // 替换为参数化查询,规避SQL注入风险 connection.query(`SELECT * FROM ShippingRate WHERE ShippingRateId = ?`, [shippingRateId], queryCallback) } else { connection.query(`SELECT * FROM ShippingRate `, queryCallback) } };
额外说明:原代码的SQL查询直接拼接参数存在SQL注入风险,上面的修复代码已经换成参数化查询写法,建议同步修改。
内容的提问来源于stack exchange,提问作者Stella Zhou
相关产品推荐
相关产品推荐

