I need to send html file (frontend is on ReactJS) with a json that contains some user info from request.
I try this, but this give me a Cannot set headers after they are sent to the client
error
app.get("/*", (req, res) => {
res.json({ message: req.*something* });
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"), (e) => {
if (e) {
console.log(`\nEROR\n${e}\nEROR\n`);
res.send(`<h1 style='text-align:center'>ERROR ${e.status}</h1>`);
}
});
});
I try to search how to do this, but get solutions only for "js views"
Is there any way to do that?
CodePudding user response:
An HTTP request can only have one HTTP response. An HTTP response can only have one body.
If I type http://example.com/
into my browser's address bar, then I get an HTML document back.
My browser wouldn't know what to do if it sent back an HTML document and a JSON text but you're trying to send back a JSON text (res.json
) and an HTML document (res.sendFile
) and a fragment of HTML (res.send
)!
You could send back a JSON text where a property on an object was a string containing an HTML document.
{ "html": "<!DOCTYPE ... etc", somethingElse: 123 }
You could send back an HTML document with JSON in the body.
You could send back an HTML document with a link to a different URL that returns JSON.
i want to render page, reactJS dinamicly renders content, there only one index.html
General, the approach to take here would be to:
- Create a directory to contain the output of building your React project
- Use
express.static
to serve that directory from the root - Create an Express router to handle providing any JSON that needs to be dynamically generated
- Mount that at a path like
/api/
- Use Ajax to make HTTP requests to the API from the React application
So your server-side could would end up looking something like:
const myApiRouter = require('./routers/api');
const express = require('express')
const app = express()
const port = 3000
app.use("/api", myApiRouter);
app.use(express.static('path/to/react/build'));
app.listen(port, () => {
console.log(`Listening on port ${port}`)
});
And then a React component would do something like:
const MyComponent = () => {
const [apiData, setApiData] = useState(null);
useEffect(() => {
fetch('/api/foo')
.then(response => response.json())
.then(data => setApiData(data));
}, []);
if (!apiData) return <Loading />;
return <ul>
{ apiData.map(item => <li>{item}</li>) }
</ul>
};
i need to send to send requested url back, for routing on frontend site (react-router-dom), if i just type localhost:8000/main, it will throw "cannot get /main" insted of /main rout on frontend, so i send request back and change route on frontend
Generally, unless you are doing SSR or static page generation, I would suggest not using BrowserRouter. Having a bunch of different URLs serve up the same content which just bootstraps different bits of client-side code isn't create for caching.
I would use HashRouter instead.
If you really want to use Browser Router, then the structure of the Express app would be similar, you just need to default to serving up the index file for anything that would otherwise 404.
app.use("/api", myApiRouter);
app.use(express.static('path/to/react/build'));
app.get("/*", (req, res) => res.sendFile("path/to/react/build/index.html"));
That said, static page generation and SRR are excellent things to do and I heartily recommend them. I generally use Next.js to achieve these. (And it has its own routing system so you'd be ditching React Router entirely for this).
If you use Static Generation then the resulting static files can be served up as per the first Express.js example at the top of this answer.
If you use SSR then you'll use the Next.js server instead of Express.
CodePudding user response:
Because when you call res.sendFile() it send index.html to client. You cannot use res.send() in callback. Why would you do that?