TL;DR: How to actually change a request header in http-proxy-middleware?
To get around some CORS errors I set up a local proxy using the http-proxy-middleware module. In addition to setting the mode of my request to "no-cors" I need to change an additional header: "Content-Type". However, this seems to be not working. In fact, I cannot even change the response headers on a redirected (through my proxy) request. For local requests (fetching pages etc) I am able to change the response headers but even then I am unable to change the request headers.
This is my setupProxy.js:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use((req, res, next) => {
req.header("Content-Type", "application/json");
res.header("Access-Control-Allow-Origin", "*");
next();
});
function onProxyReq(proxyReq, req, res) {
console.log("test 1");
proxyReq.setHeader("Content-Type", "application/json");
req.header("Content-Type", "application/json");
}
app.use(
"/api",
createProxyMiddleware({
target: "https://my-domain.com/",
changeOrigin: true,
onProxyReg: { onProxyReq },
// secure: true,
// on: {
// proxyReq: requestInterceptor(async (buffer, proxyReq, req, res) => {
// console.log("test 2");
// }),
// },
logger: console,
})
);
};
And this is the code that sends the request:
try {
let requestOptions: RequestInit = {
method: "POST",
mode: "no-cors",
headers: {
accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
email: { username },
password: { password },
}),
};
fetch("https://localhost:3000/api/path/to/login/api", requestOptions)
.then(async function (response) {
console.log(response);
if (!response.ok) {
setError("Error code: " + response.status.toString());
}
return response.json();
})
.then(function (response) {
console.log(response);
});
} catch (e) {
console.log(e);
}
I'm getting an error back from the API itself (the CORS avoidance is working):
Content type 'text/plain;charset=UTF-8' not supported
And indeed, when I use the Chrome inspector to look at the request, the request header is set to "text/plain;charset=UTF-8". I tried setting the response header content type to "text/plain" but even that remains untouched. But how can this be after routing the request through my proxy?
EDIT:
Ok so I found out part of the problem. Setting the mode to "no-cors" in my fetch request alters the headers. But this still doesn't explain why my proxy can't edit the request headers. When I remove the "no-cors" mode but copy the headers it produced, the server is giving me error 400 (bad request). This means it is not receiving the same request as before, but this baffles me since I copied all the headers manually.
EDIT2:
Actually, I found out that when I remove mode: "no-cors" and set the "Sec-Fetch-Mode" header to "no-cors" manually, it is still set to "cors" in the actual request!
EDIT3:
I tried sending my request through ReqBin and it works there :)! So at least we know my request is correct.
I found out that changing the "content-type" header in cors mode is simply not allowed. The solution is to first send a preflight request with the options. When this has been accepted, you can send the actual request.
You can send the request through ReqBin, it will take the necessary steps to complete the request succesfully. It will even generate code to reproduce the request for you.
var url = "https://thedomain.com/path/to/api";
var xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.status);
console.log(xhr.responseText);
}
};
var data_ = '{"email": "*************", "password": "******"}';
xhr.send(data_);
And this works! :)
Related
I'm trying to post an image to restdb media archive, I followed the instructions on their website https://restdb.io/blog/a-picture-is-worth-a-1000-lines-of-code
but all I get is a error
here is my code:
async function fetchPost(url, data) {
let answer
try {
const response = await fetch(url, {
method: 'post', // *GET, POST, PUT, DELETE, etc.
headers: {
'content-type': false,
'x-apikey': '62050e461b941c73ff397a3f',
},
processData: 'false',
body: data,
})
answer = await response.json()
} catch (err) {
console.log(err)
} finally {
return answer
}
}
let formData = new FormData()
formData.append('myFile', e.target.files[0], e.target.files[0].name)
fetchPost('https://mikey-f985.restdb.io/media', formData)
And Here is my API key (CORS)
Just in case you are on the free plan: I had the same issue with GET requests, so i asked restdb directly for help and they answered:
"Yes you can store images for use in development. But to serve them public you need a paid account."
So i am writing tests for one of my react projects and i just decided to use mock service worker to mock my api calls and i am trying to mock a login endpoint.So i am trying to simulate a login error where i return an error message when the input does not match a particular email. Given the code below;
const server = setupServer(
rest.post("https://testlogin.com/api/v1/login", (req, res, ctx) => {
// the issue is getting the email from the request body something like the code just below
if (req.body["email"] != "test#example.com") {
ctx.status(401);
return res(
ctx.json({
success: false
})
);
}
})
);
How can i do that? Is there a better way to do that?
You should be able to get the req.body.email value given your request sets the Content-Type: application/json header. Without the Content-Type header, neither MSW nor your actual server could know what kind of data you're attempting to send (if anything, it can be a binary!). By providing the correct Content-Type header you form a correct request but also let MSW be sure that req.body should be parsed to an object.
// your-code.js
fetch('https://testlogin.com/api/v1/login', {
method: 'POST',
headers: {
// Adding this header is important so that "req.body"
// is parsed into an object in your request handler.
'Content-Type': 'application/json'
},
body: JSON.stringify({ login: 'admin#site.com' })
})
// your-handlers.js
rest.post('https://testlogin.com/api/v1/login', (req, res, ctx) => {
const { login } = req.body
if (login !== 'test#example.com') {
return res(ctx.status(401), ctx.json({ success: false }))
}
return res(ctx.json({ success: true }))
})
Note how the ctx.status(401) call is inside the res() function call. Calling any ctx[abc] methods outside of res will result in no effect as they rely on being wrapped in res.
const https = require('https');
export async function main(event, callback) {
const options = {
method: 'GET',
host: 'https://api.challonge.com/v1/',
headers: {
'Access-Control-Allow-Methods': 'GET',
"api_key": "THE_KEY",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
}
};
var dataString = " ";
const response = await new Promise((resolve, reject) => {
const req = https.get(options, function (response) {
response.on('data', chunk => {
dataString += chunk;
});
response.on('end', () => {
resolve({
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify((dataString))
});
});
});
req.on('error', (e) => {
reject({
statusCode: 500,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: e.message
});
});
});
return response;
};
This is the lambda function ^
getChallongeTournaments:
handler: getChallongeTournaments.main
events:
- http:
path: tournaments/
method: get
cors: true
authorizer: aws_iam
my serverless.yml
// in a useEffect
function getChallongeTournaments(){
return API.get("byoc_users", '/tournaments.json');
}
async function onLoaded() {
try {
const testChallonge = await getChallongeTournaments();
^ The API call
According to the challonge docs this should receive 'Retrieve a set of tournaments created with your account.' and there is one created.
This is the CORS error I'm receiving: Access to XMLHttpRequest at 'https://m3heucf413.execute-api.us-east-2.amazonaws.com/prod/tournaments.json' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Preflight Requests and CORS flag is raised by the Browser when there is a request from a domain/port to an entire new domain/port. If you are doing this for testing, you can disable this security flag in chrome by adding the --disable-web-security flag. Just create a shortcut of chrome to desktop > Right click > Properties >In shortcut tab - Target > Append --disable-web-security --user-data-dir="C:\tmpChromeSession" to target. This will disable the CORS check.
If you have access/control on the 3rd party api server config, what you should be doing is to add necessary response headers (Access-Control-Allow-Origin) to the response. If you do not have access, one option will be to route the request through a CORS proxy.
I am getting this bizarre twilsoc error when trying to connect to twilio through node and react. I cannot figure out how to fix this. This seems to be happening on the server side of my application. I have generated the token based on the instructions on the website.
index.js:1437 Error: Can't connect to twilsock
at Upstream.send (upstream.js:245)
at TwilsockClient.post (client.js:280)
at network.js:176
at Retrier.<anonymous> (network.js:114)
at Retrier.emit (events.js:136)
at Retrier.attempt (retrier.js:56)
at retrier.js:111
Here is on the front end
componentDidMount() {
fetch("http://localhost:3001/chat/token", {
headers: { 'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin': "*",
'Access-Control-Allow-Headers': "*"
},
method: 'POST',
body: `identity=${encodeURIComponent(this.props.username)}`
})
.then(res => res.json())
.then(data => Chat.create(data.token))
.then(this.setupChatClient)
.catch(this.handleError);
}
here is the server
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(pino);
app.use(cors());
const sendTokenResponse = (token, res) => {
res.set('Content-Type', 'application/json');
res.send(
JSON.stringify({
token: token.toJwt()
})
);
};
app.get('/api/greeting', (req, res) => {
const name = req.query.name || 'World';
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ greeting: `Hello ${name}!` }));
});
app.get('/chat/token', (req, res) => {
const identity = req.query.identity;
const token = chatToken(identity, config);
sendTokenResponse(token, res);
});
app.post('/chat/token', (req, res) => {
console.log('this is firing on the backend')
const identity = req.query.identity;
const token = new AccessToken('AC821b3924fcf9577a0eb017c4b21b----', "SK8c95cf6ba0e4a0ec5499d12ae4d----", "o4x7JC9xTEAsZC06SVsnfb2xZU9n----");
const chatGrant = new ChatGrant({
serviceSid:"ISdd3f2b55594f45038ac88d84b78e----" ,
});
token.addGrant(chatGrant);
token.identity = identity;
sendTokenResponse(token, res);
});
app.get('/video/token', (req, res) => {
const identity = req.query.identity;
const room = req.query.room;
const token = videoToken(identity, room, config);
sendTokenResponse(token, res);
});
app.post('/video/token', (req, res) => {
const identity = req.body.identity;
const room = req.body.room;
const token = videoToken(identity, room, config);
sendTokenResponse(token, res);
});
app.listen(3001, () =>
console.log('Express server is running on localhost:3001')
);
the latest versions of express are not using bodyparser.json any more, it's now a part of express, try using:
express(express.json())
instead of
express(bodyParser.json())
Twilio developer evangelist here.
This actually looks like my code 😄. This is good news, because it's from my post on how to proxy to an Express server with React so you can avoid CORS issues. If you are using my repo then you should be able to start both the server and the front end applications by running:
npm run dev
Then you don't need to fetch from an absolute URL, instead you can just use:
fetch("/chat/token", {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'POST',
body: `identity=${encodeURIComponent(this.props.username)}`
});
And the webpack dev server will proxy the request through to the Express application.
Let me know if that helps at all.
Quick note on CORS
I noticed you're trying to pass the headers 'Access-Control-Allow-Origin': "*", 'Access-Control-Allow-Headers': "*" from your fetch request. However they are not request headers, but response headers. If you do need CORS headers then you need your Express server to return them as part of the response.
But as I said, the way I set up the code for this post should mean that you don't need CORS at all. So you shouldn't have to worry about this for now.
If you're using express, the easiest way to achieve this is using the cors module.
First, install it using the next code:
npm install cors
Next, put the cors middleware in the express app:
app.use(cors())
If you want to learn more, read the cors module docs https://www.npmjs.com/package/cors
If none of these solutions work (they didn't for me), here's something that might be helpful which made the error go away for me: https://stackoverflow.com/a/56671780/404541
I had this problem because I mispelled the SIDs I was using when getting the token through the rest API. I made sure the SIDs were correct and the error went away.
i am trying to send a POST request from my angularjs controller to the nodejs server which should then send a full POST request to the external API and this way avoid CORS request as well as make it more secure as i'm sending relatively private data in this POST request.
My angularjs controller function for making the post request to the nodejs server looks like this and it works fine:
var noteData = {
"id":accountNumber,
"notes":[
{
"lId":707414,
"oId":1369944,
"nId":4154191,
"price":23.84
}
]
}
var req = {
method: 'POST',
url: '/note',
data: noteData
}
$http(req).then(function(data){
console.log(data);
});
Now the problem lies in my nodejs server where i just can't seem to figure out how to properly send a POST request with custom headers and pass a JSON data variable..
i've trierd using the nodejs https function since the url i need to access is an https one and not http ,i've also tried the request function with no luck.
I know that the url and data i'm sending is correct since when i plug them into Postman it returns what i expect it to return.
Here are my different attempts on nodejs server:
The data from angularjs request is parsed and retrieved correctly using body-parser
Attempt Using Request:
app.post('/buyNote', function (req, res) {
var options = {
url: 'https://api.lendingclub.com/api/investor/v1/accounts/' + accountNumber + '/trades/buy/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': apiKey
},
data = JSON.stringify(req.body);
};
request(options, function (error, response, body) {
if (!error) {
// Print out the response body
// console.log(body)
console.log(response.statusCode);
res.sendStatus(200);
} else {
console.log(error);
}
})
This returns status code 500 for some reason, it's sending the data wrongly and hence why the server error...
Using https
var options = {
url: 'https://api.lendingclub.com/api/investor/v1/accounts/' + accountNumber + '/trades/buy/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': apiKey
}
};
var data = JSON.stringify(req.body);
var req = https.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.log(`problem with request: ${e.message}`);
});
req.write(data);
req.end();
Https attempt return a 301 status for some reasons...
Using the same data, headers and the url in Postman returns a successful response 200 with the data i need...
I don't understand how i can make a simple http request...
Please note: this is my first project working with nodejs and angular, i would know how to implement something like this in php or java easily, but this is boggling me..
So after a lot of messing around and trying different things i have finally found the solution that performs well and does exactly what i need without over complicating things:
Using the module called request-promise is what did the trick. Here's the code that i used for it:
const request = require('request-promise');
const options = {
method: 'POST',
uri: 'https://requestedAPIsource.com/api',
body: req.body,
json: true,
headers: {
'Content-Type': 'application/json',
'Authorization': 'bwejjr33333333333'
}
}
request(options).then(function (response){
res.status(200).json(response);
})
.catch(function (err) {
console.log(err);
})