【问题标题】:API routes on react + express app giving 404 on Herokureact + express 应用程序上的 API 路由在 Heroku 上给出 404
【发布时间】:2020-06-06 11:19:55
【问题描述】:

所以我构建了一个简单的 mern 应用程序,该应用程序在我的本地环境中运行良好,但是在部署到 Heroku 时,它可以很好地服务于 react 应用程序,但在 API 调用上会出现 404。我似乎无法弄清楚这个问题。我正在使用 Axios 发送请求。我检查了网络请求,它们看起来都不错,但仍然返回 404。在 postman 中测试也返回了相同的错误。

这是服务器代码...知道为什么会失败吗?

const express = require('express');
const path = require('path');
const Axios = require('axios');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
require('dotenv').config();

const PORT = process.env.PORT || 8080;
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/book';

const app = express();

// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(bodyParser.json());
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === 'production') {
  app.use(express.static('client/build'));
}

const { Schema } = mongoose;
const bookSchema = new Schema({
  info: Schema.Types.Mixed,
});
const Book = mongoose.model('Book', bookSchema);

app.post('/api/search', (req, res) => {
  Axios.get(
    `https://www.googleapis.com/books/v1/volumes?q=${req.body.term}`
  ).then(books => res.json(books.data.items));
});
app.post('/api/save', (req, res) => {
  const newBook = new Book({ info: req.body.book });
  newBook.save(err => {
    if (err) res.json(err);
    res.json({ status: true });
  });
});
app.post('/api/unsave', (req, res) => {
  Book.findByIdAndRemove(req.body.book._id, err => {
    if (err) res.json(err);
    res.json({ status: true });
  });
});
app.get('/api/saved', (req, res) => {
  Book.find({}, (err, books) => {
    if (err) res.json(err);
    res.json(books);
  });
});
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, './client/build/index.html'));
});

mongoose.connect(mongoUri, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log('connected');
});

app.listen(PORT, () => {
  console.log(`???? ==> API server now on port ${PORT}!`);
});

这是我的 package.json

{
    "name": "google-book",
    "version": "1.0.0",
    "description": "",
    "main": "server.js",
    "scripts": {
        "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev",
        "start:prod": "node server.js",
        "start:dev": "concurrently \"nodemon --ignore 'client/*'\" \"npm run client\"",
        "client": "cd client && npm run start",
        "install": "cd client && npm install",
        "build": "cd client && npm run build",
        "heroku-postbuild": "npm run build"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "axios": "^0.19.2",
        "body-parser": "^1.19.0",
        "concurrently": "^5.1.0",
        "dotenv": "^8.2.0",
        "express": "^4.17.1",
        "mongodb": "^3.5.3",
        "mongoose": "^5.9.1"
    }
}

我的反应路线以防出现问题

return (
    <div className="app">
      <Header />
      <Router>
        <Route exact path='/'>
          <Searchbar search={search} setSearch={setSearch} />
          {!search.term ? (
            <div className="message">
              <p>Search for a book or whatever</p>
            </div>
          ) : <SearchList results={search.results} />}
        </Route>
        <Route path='/saved'>
          <h2 className="title">Saved Books</h2>
          <SavedList />
        </Route>
        <Footer />
      </Router>
    </div>
  );

【问题讨论】:

  • heroku 日志中的任何内容?浏览器控制台呢?前端代码是什么样的?您愿意分享网址吗?
  • 除了 404 错误之外的任何日志都没有任何用处。这真的很奇怪,因为它在我的本地环境中运行良好(代理)。这是链接googly-book.herokuapp.com 这是请求的样子:请求 URL:googly-book.herokuapp.com/api/search 请求方法:POST 状态代码:404 未找到远程地址:18.211.160.51:80 推荐人策略:no-referrer-when-downgrade
  • Heroku 中的日志是怎么说的?
  • 2020-02-22T03:09:27.294399+00:00 heroku[router]: at=info method=POST path="/api/search" host=googly-book.herokuapp.com request_id =aa832fd7-c70e-4317-b1ed-5a8128841ee3 fwd="76.247.3.91" dyno=web.1 connect=0ms service=1ms status=404 bytes=417 protocol=http
  • 是的,这真的很奇怪......我没有什么特别的 - 一切看起来都应该正常工作。

标签: javascript node.js reactjs express heroku


【解决方案1】:

最终结果:


说明:

因此,即使在本地运行此程序时,我也会收到 404 - 问题原来是您如何启动应用程序。

您只需要启动服务器,而不需要启动客户端。看起来您正在启动 create-react-app 附带的“内置”服务器...所以,您的服务器实际上从未接受请求,因为您的前端在端口 3000 上运行,而您的后端在您拥有的任何端口上运行设置在.env

由于您使用 axios 发送请求的方式(仅使用在内置 create-react-app 端口上运行的当前 URL,而不是您的服务器端口),它本质上是向错误的端口发送请求。

这是我昨晚应该想到的,因为我记得看到您的 Heroku 应用程序正在使用 React 的开发版本(通过 Firefox React 扩展) - 这应该是一个危险信号。

我添加了 2 个新的 npm 脚​​本:npm run beginnpm start(将原来的 npm start 重命名为 npm run start:originalnpm run begin 正确构建您的前端,然后启动您的后端。这最终解决了问题。我在本地测试时也有NODE_ENV=production

我还删除了npm heroku-postbuild,因为它不需要。


代码更改:

在让它工作后,我注意到您的前端代码有问题 - 一个循环一遍又一遍地发送请求 - 我一直看到以下信息记录到控制台。所以我也解决了,再往下使用代码(我没有删除任何代码,我只是将代码注释掉,这样你就可以看到我所做的更改)。

我不知道您在哪里使用 Mongo,但我使用 Atlas 进行了测试 - 在部署到 Heroku 后与数据库通信时遇到问题,因此我还必须更改您在 @987654335 中连接到数据库的方式@。您还可以在下方或 GitHub 存储库中查看这些更改。

如果您希望我向您的仓库发送拉取请求,请告诉我,这样您就可以获得更新的代码,而无需手动更改任何内容。

最后,仔细检查 Heroku 中的环境变量 - 确保它们已设置。

// This kept being logged to the console
...
actually hit the route
actually hit the route
actually hit the route
actually hit the route
actually hit the route
actually hit the route
...
...
// This kept going on and on and on after I searched

这些是我为修复请求循环所做的更改:

// App.js

function App() {
  /**
   * Separated your state into 2 different variables.
   * Your request loop was happening due to how your 
   * useEffect was configured (specifically the dependency 
   * array)
   */
  const [searchTerm, setSearchTerm] = useState();
  const [searchResults, setSearchResults] = useState();

  /*
  const [search, setSearch] = useState({
    term: '',
    results: []
  });
  */

  useEffect(() => {
    Axios.post(`/api/search`, { term: searchTerm /* search.term */ })
    .then(books => {
      setSearchResults(books.data);
      // setSearch({...search, results: books.data})
    });
  }, [searchTerm]);

  return (
    <div className="app">
      <Header />
      <Router>
        <Route exact path='/'>
          <Searchbar /* search={search} <-- No need for this param */ setSearch={setSearchTerm} /> 
          {!searchTerm /* search.term */ ? (
            <div className="message">
              <p>Search for a book or whatever</p>
            </div>
          ) : <SearchList results={searchResults/* search.results */} />}
        </Route>
        <Route path='/saved'>
          <h2 className="title">Saved Books</h2>
          <SavedList />
        </Route>
        <Footer />
      </Router>
    </div>
  );
}
// Searchbar.js

const Searchbar = ({/* search, */ setSearch}) => { // <-- No need for search param here
    return (
        <form action="#" method="get" className="searchbar" onSubmit={e => e.preventDefault()}>
            <DebounceInput
                minLength={2}
                debounceTimeout={300}
                type="search" 
                placeholder="? search..."
                onChange={(e) => setSearch(e.target.value)}
            />
        </form>
    )
}
// server.js

require('dotenv').config();

const express = require('express');
const path = require('path');
const Axios = require('axios');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');

const PORT = process.env.PORT || 8080;
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/book';

const app = express();

// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(bodyParser.json());

// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === 'production') {
  app.use(express.static('client/build'));
}

const { Schema } = mongoose;

const bookSchema = new Schema({
  info: Schema.Types.Mixed,
});

// *** REMOVED THIS ***
// const Book = mongoose.model('Book', bookSchema);

// ==========================================================
// **********************************************************
//          CHANGED THE WAY YOU CONNECT TO MONGO
// **********************************************************
// ==========================================================
/** */ mongoose.set('useCreateIndex', true);
/** */ 
/** */ const mongoConnection = mongoose.createConnection(mongoUri, {
/** */   useUnifiedTopology: true,
/** */   useNewUrlParser: true,
/** */   useFindAndModify: false,
/** */ });
/** */ 
/** */ const Book = mongoConnection.model('Book', bookSchema /*, 'COLLECTION_NAME'*/);
// ==========================================================
// **********************************************************
//                      END OF CHANGES
// **********************************************************
// ==========================================================

app.post('/api/search', (req, res) => {
  console.log('actually hit the route');
  Axios.get(
    `https://www.googleapis.com/books/v1/volumes?q=${req.body.term}`
  ).then(books => res.json(books.data.items));
});

app.post('/api/save', (req, res) => {
  const newBook = new Book({ info: req.body.book });
  newBook.save(err => {
    if (err) res.json(err);
    res.json({ status: true });
  });
});

app.post('/api/unsave', (req, res) => {
  Book.findByIdAndRemove(req.body.book._id, err => {
    if (err) res.json(err);
    res.json({ status: true });
  });
});

app.get('/api/saved', (req, res) => {
  Book.find({}, (err, books) => {
    if (err) res.json(err);
    else res.json(books);
  });
});

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, './client/build/index.html'));
});

/*
const db = mongoose.connection;

db.on('error', // console.error.bind(console, 'connection error:') 
  error => {
    console.log("[MONGOOSE][ERROR]", error);
  }
);

db.once('open', function() {
  console.log('[MONGOOSE][SUCCESS] Connected to database!');
});
*/

app.listen(PORT, () => {
  console.log(`? ==> API server now on port ${PORT}!`);
});

【讨论】:

  • 哇,感谢您解决问题!这就是问题所在。
  • @MarcFariasJones 没问题!我无法弄清楚,这让我很困扰,哈哈。我还用一种不同的连接 Mongo 的方法更新了我的答案。我注意到,在 Heroku 上构建之后,构建实际上不会完成 - 我猜是因为打开的 Mongo 连接混淆了 Heroku 构建器,它从未想过构建完成。我更改连接以缓解此问题的方式,并且不再发生(但您仍然能够访问数据库等)
【解决方案2】:

如果您在 api 保存中添加一个 console.log,它会在点击该 url 时运行吗?即使它返回 404,它也可能仍在运行。

【讨论】:

猜你喜欢
  • 2021-07-11
  • 2021-06-15
  • 2020-09-17
  • 1970-01-01
  • 2019-12-17
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 2020-12-09
相关资源
最近更新 更多