浏览器可访问的Google Places API在JS中报TypeError: Failed to fetch
解决Google Places API前端fetch请求TypeError: Failed to fetch问题
问题核心
你遇到的本质是CORS跨域问题:浏览器地址栏直接访问API URL能返回结果,是因为这类请求不属于AJAX跨域请求范畴,不受CORS限制;但前端脚本通过fetch/axios发起的跨域请求,会被浏览器拦截——即使网络请求返回200状态码,浏览器也会因响应头缺少允许当前域名的Access-Control-Allow-Origin字段,拒绝将响应内容交给脚本,最终抛出TypeError: Failed to fetch,同时在DevTools中标记响应为红色。
解决方案
方案1:使用Google Places JavaScript SDK(推荐,更安全)
你已经在HTML中引入了Google Places SDK,但未利用它的内置服务处理跨域逻辑。SDK会自动规避CORS问题,且无需在请求URL中暴露API密钥:
修改HTML的Google Maps脚本
添加callback参数,指定SDK加载完成后执行的初始化函数:
<script src="https://maps.googleapis.com/maps/api/js?key=ENTER_KEY_HERE&libraries=places&callback=initMap"></script>
替换JavaScript代码为SDK调用逻辑
// 初始化函数,由Google SDK加载完成后自动调用 function initMap() { // 创建PlacesService实例(临时DOM元素即可满足初始化要求) const placesService = new google.maps.places.PlacesService(document.createElement('div')); // 请求参数 const request = { placeId: 'ChIJZ92Qj31CXIYR1i6OU-1uHp0', fields: ['reviews'] // 指定仅获取评论字段 }; // 调用getDetails接口获取数据 placesService.getDetails(request, (placeResult, status) => { if (status === google.maps.places.PlacesServiceStatus.OK) { console.log('获取到的评论:', placeResult.reviews); } else { console.error('获取数据失败:', status); } }); } // 将初始化函数挂载到window,供SDK回调 window.initMap = initMap;
方案2:搭建后端代理(适合自定义业务逻辑场景)
通过自有后端服务转发请求,既规避CORS问题,又能保护API密钥(前端直接暴露密钥极易被滥用,导致账户产生高额不必要费用):
示例Node.js/Express后端代码
const express = require('express'); const axios = require('axios'); const app = express(); const PORT = 3001; // 配置CORS,允许你的前端域名跨域请求 app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', 'http://localhost:8080'); // 替换为你的前端实际域名 res.header('Access-Control-Allow-Methods', 'GET'); next(); }); // 代理接口,处理前端的评论请求 app.get('/api/get-place-reviews', async (req, res) => { const { placeId } = req.query; const googleApiKey = 'YOUR_GOOGLE_API_KEY'; // 密钥存储在后端,不暴露给前端 try { const response = await axios.get('https://maps.googleapis.com/maps/api/place/details/json', { params: { key: googleApiKey, placeid: placeId, fields: 'reviews' } }); res.json(response.data); } catch (error) { res.status(error.response?.status || 500).json({ error: error.message }); } }); // 启动后端服务 app.listen(PORT, () => { console.log(`代理服务运行在 http://localhost:${PORT}`); });
前端修改fetch请求地址
async function getdata() { try { // 请求自有后端的代理接口 const response = await fetch('http://localhost:3001/api/get-place-reviews?placeId=ChIJZ92Qj31CXIYR1i6OU-1uHp0'); if (!response.ok) { throw new Error('请求失败'); } const items = await response.json(); console.log(items); } catch (err) { console.error(err); } } getdata();
关键提醒
生产环境中绝对不要在前端代码直接暴露Google API密钥,一旦被恶意获取,可能导致大量违规请求产生高额费用。上述两种方案均规避了密钥暴露风险,是合规的实现方式。
内容的提问来源于stack exchange,提问作者Lacnoskillz
相关产品推荐
相关产品推荐

