改用React重构Yelp API应用时持续出现401错误求助
React重构表单后调用Yelp API返回401错误
原本用EJS、Express、Node.js和Yelp API开发的应用,提交表单后会跳转显示随机餐厅信息的页面,EJS环境下完全正常。现在用React重构前端,后端基本未改动,nodemon测试后端能正常运行且可正确获取API密钥,但React提交表单时持续收到401错误。
原EJS应用的index.ejs表单代码
<section class="whole"> <form id="myForm" action="/selection" method="GET"> <h3>Location</h3> <input id="city" type="text" name="cityInput" placeholder="Zip or City & State" required> <!-- the rest of the input form code --> <br> <input class="button" type="submit" value="Submit"> </form> </section>
React重构后的App.js代码
import React from 'react' import Logo from './images/rest6.png' import "./style.css"; function App() { const [formData, setFormData] = React.useState( { city: "", price: "", category: "" } ) function handleChange(event) { const {name, value, type, checked} = event.target setFormData(prevChoice => { return { ...prevChoice, [name]: value } }) } function handleSubmit(event) { event.preventDefault() console.log(formData) fetch(`/selection`) .then((response) => console.log(response)); } return ( <form onSubmit={handleSubmit}> <h3>Location</h3> <input type="text" name="city" onChange={handleChange} value={formData.city || ""} placeholder="Zip or City & State" /> <h3 className="priceHeader">Price</h3> <section id="priceSection"> <label className="radioLabel" htmlFor="priceOne"> <input onChange={handleChange} id="priceOne" type="radio" value="1" name="price" checked={formData.price === "1"} required oninvalid="alert('You must fill out the form!');" /> <div className="radioButtons"> <i className="fa-sharp fa-solid fa-dollar-sign"></i> </div> </label> <label className="radioLabel" htmlFor="priceTwo"> <input onChange={handleChange} id="priceTwo" type="radio" value="2" name="price" checked={formData.price === "2"} /> <div className="radioButtons"> <i className="fa-sharp fa-solid fa-dollar-sign"></i> <i className="fa-sharp fa-solid fa-dollar-sign"></i> </div> </label> <label className="radioLabel" htmlFor="priceThree"> <input onChange={handleChange} id="priceThree" type="radio" value="3" name="price" checked={formData.price === "3"} /> <div className="radioButtons"> <i className="fa-sharp fa-solid fa-dollar-sign"></i> <i className="fa-sharp fa-solid fa-dollar-sign"></i> <i className="fa-sharp fa-solid fa-dollar-sign"></i> </div> </label> <label className="radioLabel" htmlFor="priceFour"> <input onChange={handleChange} id="priceFour" type="radio" value="4" name="price" checked={formData.price === "4"} /> <div className="radioButtons"> <i className="fa-sharp fa-solid fa-dollar-sign"></i> <i className="fa-sharp fa-solid fa-dollar-sign"></i> <i className="fa-sharp fa-solid fa-dollar-sign"></i> <i className="fa-sharp fa-solid fa-dollar-sign"></i> </div> </label> <label className="radioLabel" htmlFor="priceAll"> <input onChange={handleChange} id="priceAll" type="radio" value="1,2,3,4" name="price" checked={formData.price === "1,2,3,4"} /> <div className="radioButtons"> <p className="radioInputAll">All</p> </div> </label> </section> <h3 className="categoryHeader">Categories</h3> <section className="foodType"> <label className="radioLabel"> <input onChange={handleChange} type="radio" value="mexican" name="category" checked={formData.category === "mexican"} required oninvalid="alert('You must fill out the form!');" /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Mexican</p> </div> </label> <label className="radioLabel" htmlFor="mediterranean"> <input onChange={handleChange} type="radio" id="mediterranean" value="mediterranean" name="category" checked={formData.category === "mediterranean"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Mediterranean</p> </div> </label> <label className="radioLabel" htmlFor="sushi"> <input onChange={handleChange} type="radio" id="sushi" value="sushi" name="category" checked={formData.category === "sushi"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Sushi</p> </div> </label> <label className="radioLabel"> <input onChange={handleChange} type="radio" value="pizza" name="category" checked={formData.category === "pizza"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Pizza</p> </div> </label> <label className="radioLabel"> <input onChange={handleChange} type="radio" value="korean" name="category" checked={formData.category === "korean"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Korean</p> </div> </label> <label className="radioLabel"> <input onChange={handleChange} type="radio" value="coffee" name="category" checked={formData.category === "coffee"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Coffee</p> </div> </label> <label className="radioLabel"> <input onChange={handleChange} type="radio" value="burgers" name="category" checked={formData.category === "burgers"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">Burger</p> </div> </label> <label className="radioLabel"> <input onChange={handleChange} type="radio" value="food" name="category" checked={formData.category === "food"} /> <div className="radioButtons categoryButtons"> <p className="radioInputText">None</p> </div> </label> </section> <br /> <button>Submit</button> </form> ) } export default App;
后端index.js代码
require('dotenv').config() const express = require('express'); const app = express(); const fetch = require('node-fetch'); const yelp = require('yelp-fusion'); const ejs = require('ejs'); const bodyParser = require('body-parser'); app.set(express.static('public')); const api_key = process.env.API_KEY app.set('view engine', 'ejs'); app.use(express.static('public')); //route for index page app.get("/", function (req, res) { res.render("index"); }); const port = process.env.PORT || 3001; console.log("Running on " + port) app.listen(port, () => { console.log(`Listening on port ${port}`) }); let restaurant, city, price, category, num = 0; app.get('/selection', (req, res) => { console.log('running') city = req.query.city; price = req.query.price; category = req.query.category; console.log(Object.keys(req.query) ) console.log(Object.keys(req.params) ) console.log(Object.keys(req) ) console.log("category: " + category) console.log("city: " + city) console.log("price: " + price) console.log("RES: " + res.locals.user); const url = `https://api.yelp.com/v3/businesses/search?location=${city}&categories=${category}&price=${price}&sort_by=best_match&limit=50` console.log(url) const options = { method: 'GET', headers: { accept: 'application/json', Authorization: `Bearer ${api_key}` } }; fetch(url, options) .then(response => response.json()) .then(response => { console.log("result: ", response); num = Math.floor(Math.random() * (Object.keys(response.businesses).length)); console.log(response.businesses[num]) console.log("Number of Choices: " + (Object.keys(response.businesses).length)) console.log("random number: " + num) restaurant = response.businesses[num] res.render('rest', { restaurant: restaurant }) res.status(200).send(response); }) .catch(err => { res.status(401).send(err); console.error(err) }); });
问题原因及解决办法
1. 表单数据未传递到后端
React的handleSubmit中,fetch('/selection')未携带任何查询参数,后端req.query无法获取city、price、category,导致拼接的Yelp API URL中关键参数为空,触发Yelp API权限校验失败返回401。
修复代码:
将formData转换为查询字符串拼接到请求URL后:
function handleSubmit(event) { event.preventDefault() console.log(formData) const params = new URLSearchParams(formData); fetch(`/selection?${params}`) .then((response) => response.json()) .then(data => console.log(data)) .catch(err => console.error(err)); }
2. 后端重复发送响应
后端/selection路由中,先调用res.render('rest', { restaurant: restaurant }),又调用res.status(200).send(response);,会导致"Cannot set headers after they are sent to the client"错误,同时干扰正常响应逻辑。
修复代码:
移除res.render(React前端需自行处理JSON数据渲染,无需后端渲染页面):
// 替换原响应代码 res.status(200).send(restaurant); // 或根据前端需求发送完整response对象
3. 后端参数校验缺失
未对req.query中的必填参数做校验,空参数传递给Yelp API会引发错误。
修复代码:
在/selection路由开头添加参数校验:
app.get('/selection', (req, res) => { console.log('running') city = req.query.city; price = req.query.price; category = req.query.category; // 新增参数校验 if (!city || !price || !category) { return res.status(400).send('缺少必要参数:城市、价格、分类'); } // 后续逻辑保持不变 // ... });
内容的提问来源于stack exchange,提问作者liverpool09
相关产品推荐
相关产品推荐

