Django+React前后端架构下带查询参数调用第三方API的正确实现方式
方案总览
你当前的实现存在不必要的请求冗余:后端从OpenWeatherMap拉取到数据后,无需额外存储后再等待前端发起GET请求拉取,完全可以在接收前端POST请求的同一次HTTP响应中直接返回处理好的天气数据,整个链路仅需2次HTTP调用(前端→你的后端、你的后端→OpenWeatherMap),没有多余往返。
具体实现步骤
Django后端侧调整
你已经完成了后端请求OpenWeatherMap的核心逻辑,只需要调整视图的响应逻辑即可,无需额外新增存储、二次接口:
- 固定天气查询接口路径,例如
/api/weather/,仅允许POST方法请求 - 视图入口先从POST请求体中解析前端传入的城市名参数,做基础非空校验
- 从服务端环境变量读取OpenWeatherMap密钥(禁止硬编码到代码中),携带城市名向第三方接口发起请求,注意设置请求超时时间
- 拿到第三方返回结果后做字段裁剪,只保留前端需要展示的核心字段,不要透传第三方返回的全量冗余数据
- 异常场景(参数为空、城市不存在、第三方接口请求失败)统一返回对应错误码和可读提示,正常场景直接在当前请求的响应中返回处理好的天气JSON数据
- 安装
django-cors-headers配置跨域规则,允许前端服务地址的请求,避免本地调试时被浏览器拦截
参考视图代码(基于Django REST Framework实现):
# views.py import os import requests from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status OWM_API_KEY = os.getenv("OWM_API_KEY") # 从环境变量读取密钥,不要硬编码 class WeatherQueryView(APIView): def post(self, request): city_name = request.data.get("city", "").strip() if not city_name: return Response({"error": "城市名不能为空"}, status=status.HTTP_400_BAD_REQUEST) try: owm_resp = requests.get( "https://api.openweathermap.org/data/2.5/weather", params={ "q": city_name, "appid": OWM_API_KEY, "units": "metric", "lang": "zh_cn" }, timeout=10 ) owm_resp.raise_for_status() raw_data = owm_resp.json() # 裁剪前端需要的字段 processed_data = { "city": raw_data["name"], "temp": raw_data["main"]["temp"], "weather_desc": raw_data["weather"][0]["description"], "humidity": raw_data["main"]["humidity"], "wind_speed": raw_data["wind"]["speed"] } return Response(processed_data, status=status.HTTP_200_OK) except requests.exceptions.RequestException: return Response({"error": "查询失败,请检查城市名或稍后重试"}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
配置路由时将上述视图绑定到/api/weather/路径即可。
React前端侧调整
前端不需要拆分POST提交、GET拉取两步逻辑,单次请求即可完成全流程:
- 通过受控组件存储用户输入的城市名,搭配输入框、查询按钮组成基础表单
- 触发查询时先开启loading状态,避免用户重复点击,同时清空历史结果和错误提示
- 用axios向自己的Django接口发POST请求,请求体携带用户输入的城市名
- 请求成功后将返回的天气数据存入组件state,渲染到页面;请求失败时展示后端返回的错误提示
- 请求完成后无论成功失败都关闭loading状态
参考组件代码:
// WeatherQuery.jsx import { useState } from 'react'; import axios from 'axios'; export default function WeatherQuery() { const [city, setCity] = useState(''); const [weatherInfo, setWeatherInfo] = useState(null); const [loading, setLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(''); const handleSearch = async (e) => { e.preventDefault(); const targetCity = city.trim(); if (!targetCity) { setErrorMsg('请输入要查询的城市名'); return; } setLoading(true); setErrorMsg(''); setWeatherInfo(null); try { const res = await axios.post('/api/weather/', { city: targetCity }); setWeatherInfo(res.data); } catch (err) { setErrorMsg(err.response?.data?.error || '网络异常,请稍后重试'); } finally { setLoading(false); } }; return ( <div> <form onSubmit={handleSearch}> <input type="text" value={city} onChange={(e) => setCity(e.target.value)} placeholder="输入城市名查询天气" /> <button type="submit" disabled={loading}> {loading ? '查询中...' : '查询天气'} </button> </form> {errorMsg && <p style={{color: 'red'}}>*{errorMsg}</p>} {weatherInfo && ( <div> <h3>{weatherInfo.city} 实时天气</h3> <p>天气状况:{weatherInfo.weather_desc}</p> <p>当前温度:{weatherInfo.temp}℃</p> <p>相对湿度:{weatherInfo.humidity}%</p> <p>风速:{weatherInfo.wind_speed}m/s</p> </div> )} </div> ); }
本地调试时可以在React配置中添加代理规则,将/api开头的请求转发到Django默认运行的http://localhost:8000地址,省去跨域配置的麻烦。
注意事项
- OpenWeatherMap密钥必须存在服务端环境变量中,禁止提交到代码仓库,也禁止在前端代码中出现
- 后端请求第三方接口必须设置合理的超时时间,避免第三方服务异常拖垮自身服务
- 可以给天气查询接口添加简单的频率限制,避免被恶意刷量消耗第三方API的调用额度
- 生产环境建议将前端打包后的静态文件交由Nginx或Django托管,不存在跨域问题
内容的提问来源于stack exchange,提问作者Prajwal Iyer
相关产品推荐
相关产品推荐

