React+Node.js城市景点查询:Mapbox API报错与响应精简需求
解决方案
一、修复Mapbox API 422错误
你遇到的422错误是因为Mapbox Geocoding API的types参数不接受单独的park值。Mapbox的POI类型有严格命名规则:
- 要指定公园类景点,用
poi.park - 要覆盖所有景点类POI(含湖泊、博物馆等),直接用
poi - 还可以组合多个具体类型,比如
poi.park,poi.lake
修正后的Node.js请求示例(以axios为例)
const axios = require('axios'); async function fetchCityAttractions(city) { try { const apiRes = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(city)}.json`, { params: { access_token: '你的Mapbox令牌', types: 'poi.park,poi.lake', // 按需调整类型 limit: 10 // 限制返回结果数量,减少冗余 } }); // 过滤提取核心景点信息 const cleanAttractions = apiRes.data.features.map(item => ({ name: item.text, fullAddress: item.place_name, coordinates: item.center, category: item.properties?.category || '未分类' })); console.log(cleanAttractions); return cleanAttractions; } catch (err) { console.error('请求失败:', err.response?.data.message || err.message); } }
二、过滤冗余响应数据
Mapbox原始响应包含大量元数据,只需从features数组提取所需字段即可。上面的代码通过map方法筛选出名称、地址、坐标、分类等核心信息,完全剔除冗余内容。
三、React前端交互示例
import { useState } from 'react'; function AttractionFinder() { const [inputCity, setInputCity] = useState(''); const [attractions, setAttractions] = useState([]); const handleSearch = async () => { if (!inputCity.trim()) return; try { const res = await fetch(`/api/attractions?city=${encodeURIComponent(inputCity)}`); const data = await res.json(); setAttractions(data); } catch (err) { console.error('搜索出错:', err); } }; return ( <div className="search-container"> <input type="text" value={inputCity} onChange={(e) => setInputCity(e.target.value)} placeholder="输入城市名" /> <button onClick={handleSearch}>获取景点</button> <ul className="attraction-list"> {attractions.map((item, index) => ( <li key={index}> <h4>{item.name}</h4> <p>{item.fullAddress}</p> <span>类型: {item.category}</span> </li> ))} </ul> </div> ); } export default AttractionFinder;
四、Node.js后端代理路由(Express)
const express = require('express'); const router = express.Router(); const axios = require('axios'); // 从环境变量读取令牌,避免硬编码泄露 const MAPBOX_TOKEN = process.env.MAPBOX_ACCESS_TOKEN; router.get('/attractions', async (req, res) => { const { city } = req.query; if (!city) return res.status(400).json({ error: '请提供城市名称' }); try { const apiRes = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(city)}.json`, { params: { access_token: MAPBOX_TOKEN, types: 'poi.park,poi.lake', limit: 10 } }); const filteredData = apiRes.data.features.map(item => ({ name: item.text, fullAddress: item.place_name, category: item.properties?.category || '未分类' })); res.json(filteredData); } catch (err) { res.status(err.response?.status || 500).json({ error: err.response?.data.message || '获取景点失败' }); } }); module.exports = router;
关键注意事项
- 绝对不要在前端代码中暴露Mapbox令牌,必须通过后端代理请求,防止令牌被盗用
- 若需要更精准的结果,可以先获取城市的边界范围(用Mapbox反向地理编码),再通过
bbox参数限定搜索范围,避免返回其他城市的景点 - 可根据需求扩展
types参数,比如添加poi.museum、poi.historical_landmark等具体类型
内容的提问来源于stack exchange,提问作者user21687269
相关产品推荐
相关产品推荐

