I have a simple express server setup like:
app.use(bodyParser.json());
app.use(cookieParser());
app.use(csurf({ cookie: true }));
// routes
app.use(Routes imported from another file);
The client is currently just a simple form in react. I am loading some initial data before the react app loads and the csrf cookie is being set there.
I have a simple function for parsing the csrf cookie client side. I'm proxying the express server in create-react-app so I can't just set a meta tag in the header.
const csrfToken = () => {
const cookies = decodeURIComponent(document.cookie).split(';');
const token = cookies.find(cookie => cookie.includes('_csrf'));
if (token) {
return token.split('=')[1]
}
}
I am using fetch to send along data and the token
const response = await fetch(url, {
credentials: 'include',
method: 'POST',
headers: {
'Connection': 'keep-alive',
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken()
},
body: JSON.stringify({ ...body })
});
I've tried commenting out the line that tells the app to use csurf and checking that everything is present on the request. I can verify that the cookie and the header are matching in every request I send. Everything seems correct, but I am still getting a 403 error so I must be missing something. I'm at a lost to what it could be and all I could find googling is other people setting up their apps very similarly.
You are reading the content of the _csrf cookie and sending it back inside X-CSRF-Token header. This will not work.
The csurf middleware running inside Express has been configured by this code: app.use(csurf({ cookie: true })); to generate the _csrf cookie and send it to the client. The middleware expects you to:
Generate the second piece of CSRF data on the server.
Attach the second piece of data to the response sent to a client. As a result, the response arrives to the client with both the _csrf cookie and the second piece of data attached.
Ensure the incoming request from the client has the same _csrf cookie and the second piece of data copied into one of the six predefined places/locations (such as 'X-CSRF-Token' header or another location).
See this answer for more details.
Notice: only when I use form-data body form in Postman (which is the form I have to use because I want to send files beside text fields), I get:
Error: Multipart: Boundary not found.
when I use x-www-form-urlencoded everything is ok. (ofcourse when body-parser is used as middleware)
This is Request Content: (made by Postman)
POST /test HTTP/1.1
Host: localhost:3000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: 61089a44-0b87-0530-ca03-a24374786bd1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="test"
a simple word
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"
good
------WebKitFormBoundary7MA4YWxkTrZu0gW--
index.js:
var express = require('express');
var app = express();
var multer = require('multer');
var upload = multer();
app.post('/test', upload.array(), function (req, res, next) {
console.log(req.body.test);
console.log(req.body);
});
app.listen(3000, function () {
console.log('app started');
});
I found the solution. I only had to prevent Postman to send a Content-Type header. So I just removed it from request headers.
To give some insight on why that is happening,
When using content type multipart/form-data in any HTTP request, you can add a boundary information alongside the Content-Type specification like:
Content-Type: multipart/form-data; boundary=MyBoundary
You can replace MyBoundary with any string of your liking.
Then you will have to encode your form data (name=Abebe&age=5) as:
--MyBoundary
Content-Disposition: form-data; name="name"
Abebe
--MyBoundary
Content-Disposition: form-data; name="age"
5
--MyBoundary--
For more info read this StackOverflow question and answer
For JMeter and postman remove Content-Type from header.
it will resolve your issue.
I am going to expand a little bit on user9150719 for those who are having the same issue with the frontend side of things and are wondering where to remove the headers.
I had the same issue; I was trying to post from an Angular app to my Nodejs server. my post request included raw data and a file input. So I was thinking FormData()
Angular Service
//Declare header variables.
formDataHeader = {
headers: new HttpHeaders({
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + this._myService.getToken()
})
};
//Post function to Nodejs server
addNewContact(contact: FormData): any {
return this._httpClient.post(
environment.apiBaseUrl + '/contacts', // POST /api/contacts
(contact), // contact data,
this.formDataHeader
);
}
My formData was setup properly. I was able to get all the data, but the problem is that I had setup couple headers in my request that resulted in what user9150719 was experiencing.
My solution was to simplify my headers to this:
formDataHeader = {
headers: new HttpHeaders({
Authorization: 'Bearer ' + this._myService.getToken()
})
};
Another important thing to point out is that I didn't need to set the enctype="multipart/form-data" on my <form></form> tag.
Even though I had an httpInterceptor setup (I don't think it is working properly), I still needed to add the Authorization header on all my requests, but all other headers were resulting in my api call to return unexpected results.
Finally I think (but I am not entirely sure) that the reason why I didn't need to setup extra headers, is because in my NodeJS server, I already configured what headers to expect.
Node.JS Server
// app.js
app.use('/public/uploads', express.static('uploads'));
app.use('/public', express.static('public'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'x-www-form-urlencoded, Origin, X-Requested-With, Content-Type, Accept, Authorization, *');
if (req.method === 'OPTIONS'){
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, PATCH, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Credentials', true);
return res.status(200).json({});
}
next();
});
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(bodyParser.json({limit: '50mb', extended: true}));
So I think that if your server is setup to handle certain types of headers (Content-Type, Authorization, Origin, etc.), You don't necessarily need to set those headers again on your frontend when you send your request to the server. There are certain exceptions, such Authorization which in certain cases need to be set; probably because they carry some data in the form of token or something in that regards.
I hope this helps someone out there!
If you are using fetch method
fetch("http://localhost:4000/upload_files", {
method: 'POST',
body: formData,
headers: {
"Content-Type": "multipart/form-data"
}
})
headers so that Fetch api automatically set the headers. Now remove headers or "Content-Type": "multipart/form-data"
fetch("http://localhost:4000/upload_files", {
method: 'POST',
body: formData
})
Now it works
Don't mention CONTENT-TYPE header while uploading files from FE using axios/fetch or anything 3rd HTTP handler.
Reason bcoz we don't know the boundary of our files. If you pass only 'multipart/form-data' as Content-Type, you will get an error since we aren't passing boundary in there.
So, let the browser add it (multipart/form-data) along with Boundary based on the files attached.
AND if you want to send some data along with files, you should be sending them as a multipart/form-data(Again we don't need to add this header manually) type only.
We CANNOT send multiple Content-Type data at once for any http call.
Please refer below code.
async onUpload(){
const formData=new FormData();
formData.append('files', file1);
formData.append('files', file2);
formData.append('data', {key1:value1, key2:value2});
const res=await axios.post(URL, formData);
}
FYI #hassam-saeed
Just if someone has the same issue i had.
NestJs - BackEnd
NextJs - FrontEnd
I was trying to do something like:
const formData = new FormData()
formData.append('context', body.context.toString())
body.files.map((file) => {
formData.append('files', file, `${file.name}`)
})
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
...(access_token ? { Authorization: `Bearer ${access_token}` } : {}),
},
body: formData,
})
But because this 'Content-Type' overrides the browsers setting of 'Content-Type' AND the content-length is not explicitly set (which was the real issue i think) ,the form-data was showing up on the backend still encoded. So NestJS was not able to parse the 'context' variable or the 'files'.
Also, if you use api routes in NextJS, remeber to check that no-where in there is the 'Content-Type' overridden.... I also had this issue.
I did this instead
const form = new FormData();
headers['Content-Type'] = `multipart/form-data; boundary=${form._boundary}`;
Source: https://stackoverflow.com/a/54796556/8590519
No matter what I try to use to set the headers it simply won't work.
The server side needs to send the response headers like this, after accepting the POST request from Frontend.
server.js
app.post('/register', (req, res) => {
res.set({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
});
res.send('string');
res.end();
}
The res.set() function was not the only function I tried to use.
I tried with:
res.writeHead(201, {'Access-Control-Allow-Origin': '*'}) - doesn't work
res.setHeader('Access-Control-Allow-Origin', '*') - nope
res.headers() - also doesn't work
It says here that it goes like this: How can I set response header on express.js assets but the answer doesn't work for me.
No matter what I tried, the server just won't respond with the CORS enabled header and I need that header property. What am I doing wrong?
Try using the cors module: https://www.npmjs.com/package/cors
var cors = require('cors')
...
app.use(cors())
The Access-Control-Expose-Headers response header indicates which headers can be exposed as part of the response by listing their names.
You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
res.set('Access-Control-Expose-Headers', 'Access-Control-Allow-Origin, <any-other-custom-header>, ...');
I have an android app sending me accelerometer data using the following where body is a string like {"device_name":"device1","time":123123123,"acceleration":1} :
con = (HttpURLConnection) new URL(SERVER).openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
writer = new OutputStreamWriter(con.getOutputStream());
writer.write(body);
writer.flush();
On the server side, I am using body parser like:
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended:false}));
...
app.post('/' function(req,res {
console.log(req.headers);
console.log(req.body);
When I get a post request, this is what shows up:
{ '{"device_name":"device1","time":123,"jerk":21.135843,"acceleration":1}': '' }
I would like to get the req.body in the form of {"device_name":"device1","time":123123123,"acceleration":1} is there a parameter I am missing to set this?
Thanks!
Update:
The client code is inaccessible for change to me, so it'll be much harder to change content type that's being sent. Here's the req.head log...
{ 'user-agent': '...(Linux; U; Android 4.1.2;...)',
host: '...',
connection: 'Keep-Alive',
'accept-encoding': 'gzip',
'content-type': 'application/x-www-form-urlencoded',
'content-length': '...' }
You're uploading a JSON string, but you don't instruct body-parser to handle those.
Instead of this:
app.use(bodyParser.urlencoded({extended:false}));
Use this:
app.use(bodyParser.json());
Also make sure that your request sets the Content-Type header to application/json. If that's not possible, and you're sure that the uploaded content is always going to be JSON, you can force the body parser to parse the body as JSON like this:
app.use(require('body-parser').json({ type : '*/*' }));
I'm using node-http-proxy and want to watch for a particular response
header and rewrite it if necessary. Anyone here have suggestions on to
do this?
My proxy server sits in front of a couple different node servers as
well as a java webapp. The java app is setting a cookie, but the
cookie has a path that is relative the the webapp's context. I need
the cookie to be secure and have a path to root without modifying the Java
application.
In other words, the following header is returned:
set-cookie: MYSPECIALCOOKIE=679b6291-d1cc-47be; Path=/app; HttpOnly
And I'd like to rewrite the Path value to:
set-cookie: MYSPECIALCOOKIE=679b6291-d1cc-47be; Path=/; HttpOnly; Secure
I'm not clear how I would do this using node-http-proxy. Suggestions?
Is there middleware to help with this?
You can achieve this by overloading the writeHead function of the response object. For example, this code will set the 'foo' response header to the value 'bar'. I've indicated where you can add your own logic to change the header values.
JavaScript is not my primary language, so there may be a more idiomatic way to overload the writeHead method.
httpProxy = require('http-proxy');
httpProxy.createServer(function (req, res, proxy) {
res.oldWriteHead = res.writeHead;
res.writeHead = function(statusCode, headers) {
/* add logic to change headers here */
var contentType = res.getHeader('content-type');
res.setHeader('content-type', 'text/plain');
// old way: might not work now
// as headers param is not always provided
// https://github.com/nodejitsu/node-http-proxy/pull/260/files
// headers['foo'] = 'bar';
res.oldWriteHead(statusCode, headers);
}
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 3000
});
}).listen(8000);
Just listen to proxyRes event and put your logic.
proxy.on('proxyRes', (proxyRes, req, res) => {
// modifying headers goes here
});
See https://www.npmjs.com/package/http-proxy#listening-for-proxy-events
I didn't test this code, but it should allow you to edit your header before sending the request. Let me know if it works.
var httpProxy = require('http-proxy');
var server = httpProxy.createServer(function (req, res, proxy) {
var buffer = httpProxy.buffer(req);
req.headers['x-host'] = process.env.PROXY_URI;
proxy.proxyRequest(req, res, {
host: '127.0.0.1',
port: 9000,
});
});