You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

改用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 21:37:36