Маршруты API на приложении реакции + express дают 404 на Heroku - PullRequest
1 голос
/ 22 февраля 2020

Итак, я создал простое приложение mern, которое прекрасно работает в моей локальной среде, но при развертывании в Heroku оно отлично работает с приложением реагировать, но на вызовах API 404s. Я не могу понять проблему. Я отправляю запросы, используя Ax ios. Я проверил сетевые запросы, и все они выглядят хорошо, но все равно возвращаются 404. Тестирование в почтальоне также вернуло ту же ошибку.

Вот код сервера ... Есть идеи, почему это не удается?

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}!`);
});

и вот мой пакет. 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>
  );

Ответы [ 2 ]

1 голос
/ 23 февраля 2020

Окончательные результаты:


Объяснение:

Итак, даже при локальном запуске я получал 404 - проблема заключалась в том, как вы запускали приложение.

Вам нужно только запустить сервер, а не клиент. Похоже, вы запускаете «встроенный» сервер, который поставляется с create-react-app ... так что ваш сервер никогда не принимал запросы, так как ваш интерфейс работал на порту 3000, а ваш сервер работал на том порту, который у вас был установить в .env.

Из-за того, что вы axios отправляете запросы (просто используя текущий URL, который работал на встроенном порту create-react-app, а не на порту вашего сервера), он по сути отправлял запросы на неправильный порт .

Это то, о чем я должен был подумать вчера вечером, так как я вспомнил, что видел, как ваше приложение Heroku использовало сборку разработки React (через расширение Firefox React) - это должен был быть красный флаг.

Я добавил 2 новых npm скрипта: npm run begin и npm start (переименовал оригинал npm start в npm run start:original. npm run begin, правильно строит ваш интерфейс, а затем запускает ваш бэкэнд . В конечном итоге это и решило проблему . У меня также было NODE_ENV=production при локальном тестировании.

Я также удалил npm heroku-postbuild, так как он не нужен.


Изменения кода:

После того, как это сработало, я заметил, что с вашим интерфейсным кодом что-то не так - al oop отправлял запрос снова и снова - я продолжал видеть ниже информация записывается на консоль. Поэтому я также решил, что используя код ниже (я не удалял код, я просто закомментировал код, чтобы вы могли видеть сделанные мной изменения).

Я не знаю, где вы ' Я использую Mon go в, но я протестировал это с помощью Atlas - у меня были проблемы с базой данных после развертывания в Heroku, поэтому мне также пришлось изменить способ подключения к базе данных в server.js. Вы также можете просмотреть эти изменения ниже или в репозитории 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

Это изменения, которые я сделал, чтобы исправить запрос л oop:

// 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}!`);
});
0 голосов
/ 22 февраля 2020

если вы добавляете console.log в сохранение API, он запускается при нажатии на этот URL? Даже если он возвращает 404, возможно, он все еще работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...