问题
After deploying my mern app to Heroku, the GET
request on the home page ('http://localhost:8000/post/')
is now returning index.html
instead of json data
from the request. I'm getting 200 status
code but the response is html
. However, it works fine locally.
All the other requests are working except this one.
Whenever I think I've fixed it, Heroku displays the json data instead of the UI on this same route. I'm assuming that these issues are related.
How can I solve this? Thanks!
route/controller - list posts
router.get('/', (list))
exports.list = (req, res) => {
const sort = { title: 1 };
Post.find()
.sort(sort)
.then((posts) => res.json(posts))
.catch((err) => res.status(400).json("Error: " + err));
};
server.js
require("dotenv").config();
// import routes
...
const app = express();
// connect db - first arg is url (specified in .env)
const url = process.env.MONGODB_URI
mongoose.connect(url, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false,
});
mongoose.connection
.once("open", function () {
console.log("DB Connected!");
})
.on("error", function (error) {
console.log("Error is: ", error);
});
// middlewares
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", '*');
res.header("Access-Control-Allow-Credentials", true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header("Access-Control-Allow-Headers", 'Origin,X-Requested-With,Content-Type,Accept,content-type,application/json');
next();
});
// middleware
...
// app.use(express.static(path.join(__dirname, './client/build')))
app.use(authRoutes);
app.use(userRoutes);
app.use('/post', postRoutes);
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
const port = process.env.PORT || 80;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
ListPosts.js
class ListPosts extends React.Component {
state = {
title: '',
body: '',
date: '',
posts: []
}
componentDidMount = () => {
this.getPosts()
}
getPosts = () => {
axios.get(`${API}/post`)
.then((response) => {
const data = response.data
this.setState({posts: [data]})
console.log(data)
})
.catch((error) => {
console.log(error)
})
}
displayPosts = (posts) => {
if (!posts.length) return null;
posts.map((post, index) => (
<div key={index}>
...
</div>
))
}
render() {
return (
<div>
{this.displayPosts(this.state.posts)}
</div>
)
}
}
export default ListPosts
回答1:
As some of the answers already mentioned seperating your API and client routes and found the exact issue, I would like to just add a little bit of recommendations based on my experience with serving your react app using express
. (Trick is to also add versioning)
app.use('/api/v1/auth', authRoutes);
app.use('/api/v1/user', userRoutes);
app.use('/api/v1/post', postRoutes);
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, "client/build")));
app.get("/*", (_, res) => {
res.sendFile(path.join(__dirname, "client/build", "index.html"));
});
}
回答2:
Your request 'http://localhost:8000/'
matches two route handler
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
router.get('/', (list))
Since your client build route is placed above the list route it will always return the index.html because precedence matters in express when defining routes.
A good practice and solution is to always differentiate your api routes from the static ones by appending /api
before all routes as below
app.use('api/auth', authRoutes);
app.use('api/post', postRoutes);
app.use('api/user', userRoutes);
回答3:
router.get('/', (list))
const list = (req, res) => {
const sort = { title: 1 };
Post.find()
.sort(sort)
// .limit(10)
.then((posts) => res.json(posts))
.catch((err) => res.status(400).json("Error: " + err));
};
module.exports = router;
In server.js
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
....
app.use(authRoutes);
app.use(userRoutes);
app.use(postRoutes);
You need to update like this.
It's just sequence problem in express middlewares. You have updated middlewares about get method(/*). So it's always returning index.html instead of JSON.
回答4:
In your current implementation you have 2 app.get for the same path -> '/' So, express responds with the first one. Which right now is:
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
You can either, specify a different path
app.use("/post", postRoutes);
or rearrange the sequence.
app.use(authRoutes);
app.use(userRoutes);
app.use(postRoutes);
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "./client/build/index.html"));
});
or change the controller
router.post('/' ...) // instead of router.get('/'...)
You need to specify the routes url or avoid two 'app.get' for the same route. If you want you can also change the controller from 'app.get' to 'app.post' and express will deem them different. But, if both are app.get for the same route, the first one will send the response and the second one will never be called.
What you can do is, first try to re-arrange the sequence. If it works and that is indeed the problem, don't stick with it as a solution. It's the wrong way to go about. Instead, give your routes a different url or change the controller from 'app.get' to 'app.post'
来源:https://stackoverflow.com/questions/64496815/get-request-returns-index-html-doc-instead-of-json-data