CORS POST request throws on Authorization header - javascript

I'm trying to send a CORS POST request to my API and it throws a TypeError every time I use the 'Authorization' header. The request doesn't even get sent, so the server is not involved. But this only happens in my tests. When I try it in Chrome it works just fine.
Here is the function that I'm testing:
export const postNewEmployee = formData => {
return fetch('http://localhost:3003', {
method: 'POST',
headers: {
'Authorization': 'Bearer test123',
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
.then(response => response)
.catch(error => {
throw error;
});
};
And its test:
import * as API from './api';
describe('postNewEmployee', () => {
it('posts the form data asynchronously', () => {
let formData = {
employee: {
name: 'Test Person',
email: 'test#person.nu',
address: 'an adress 123'
}
};
return API.postNewEmployee(formData)
.then(json => {
expect(json.status).toEqual(201);
}).catch(error => {
console.log(error);
});
});
});
The application is a react/redux app created with create-react-app, so I'm using Jest and JSDOM to test this. The thing is, if I comment out the Authorization header from the fetch()-call, it works fine. But if I add that header I get this:
TypeError: Cannot convert undefined or null to object
at Object.getRequestHeader (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:20:23)
at setDispatchProgressEvents (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:909:38)
at XMLHttpRequest.send (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:700:11)
at /Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/whatwg-fetch/fetch.js:429:11
at Object.<anonymous>.self.fetch (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/whatwg-fetch/fetch.js:373:12)
at Object.<anonymous>.exports.postNewEmployee.formData [as postNewEmployee] (/Users/johanh/Kod/react-app/src/api/api.js:20:10)
at Object.it (/Users/johanh/Kod/react-app/src/api/api.test.js:75:16)
at Object.<anonymous> (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/jest-jasmine2/build/jasmine-async.js:42:32)
at attemptAsync (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/jest-jasmine2/vendor/jasmine-2.4.1.js:1919:24)
at QueueRunner.run (/Users/johanh/Kod/react-app/node_modules/react-scripts/node_modules/jest-jasmine2/vendor/jasmine-2.4.1.js:1874:9)
And as I said, this only happens in the test. In the browser it works fine.
I feel like I'm missing something obvious here, but I just can't see it. I've looked in the fetch spec and the jsdom documentation, but to no avail. Any ideas?

Normally you should not make real requests in a unit test. The best way to handle this is to use a mock instead of the real fetch implementation.
I assume you are using the JS implementation of fetch. So you can set fetch to what ever you want in your test.
import * as API from './api';
describe('postNewEmployee', () => {
it('posts the form data asynchronously', () => {
// set fetch to a mock that always returns a solved promise
const fetch = jest.fn((url, options) => return Promise.resolve({status: 201}))
global.fetch = fetch;
let formData = {
employee: {
name: 'Test Person',
email: 'test#person.nu',
address: 'an adress 123'
}
};
//test that fetch was called with the correct parameters
expect(fetch.mock.calls[0][0]).toBe('http://localhost:3003')
expect(fetch.mock.calls[0][1]).toEqual(formData)
return API.postNewEmployee(formData)
.then(json => {
expect(json.status).toEqual(201);
}).catch(error => {
console.log(error);
});
});
});

Related

axios GET request with form data in React JS

I want to implement the following cURL request (which is working) in react js using axios:
curl -k --request GET "BASE_URL_SERVER/sendText" --form "user_id="uidxxxx"" --form "sign_id="
I always get the same error: field sign_id not found, but technically I'm sending it, so I'm kind of desesperate.
var data = new FormData();
data.append('user_id', 'uidxxxx');
data.append('sign_id', '9');
const api = axios.create({
baseURL: BASE_URL_SERVER,
data: data,
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`
},
timeout: 10000,
})
api.get('/sendText')
.then(response => console.log(JSON.stringify(response.data)))
.catch(error => { console.log(error) })
I've also tried adding '...getHeaders()' to the headers section but React says it is not a function; I've read in other posts that it has something to do with the browser
thanks in advance
ps: it is a pretty similar problem to this one, but none of the solutions worked for me
[UPDATE]
I ended up implementing it with POST, which is better for posting Form Data; no headers are needed, the browser automatically adds them:
var data = new FormData();
data.append('user_id', user_id);
data.append('sign_id', sign_id);
const api = axios.create({
baseURL: BASE_URL_SERVER,
timeout: TIMEOUT_SERVER,
})
api.post('/sendText', data)
.then(response => console.log(JSON.stringify(response.data)))
.catch(error => { console.log(error) })
You have a mistake, you try to send data via axios for POST and method is GET...
So that, You need to Change Method to be POST to can Post form data or you need to change it to url param or url path base on your api to be WORK as a GET...
Base on your curl, your case is you need a GET:
// Make a request for a user with a given ID
axios.get('/sendText?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
// Optionally the request above could also be done as
axios.get('/user', {
params: {
sendText: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
Also, you can save all config in instance and share it for all nested of write it again and again..
for example:
// Common Axios Instance Config
const axiosConfig = {
baseURL: process.env.REACT_APP_API_ENDPOINT,
};
// Create Default Axios Instace
const instance = axios.create(axiosConfig);
I think base on your example this will work, but not sure sine I'm not test it..:
var data = new FormData();
data.append('user_id', 'uidxxxx');
data.append('sign_id', '9');
const api = axios.create({
baseURL: 'https://193.146.38.4:56076',
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`
},
timeout: 10000,
})
api.get('/sendText', {
user_id: 111,
sign_id: 2222
)
.then(response => console.log(JSON.stringify(response.data)))
.catch(error => { console.log(error) })
For more details view this url

How to manage 429 errors in axios with react native?

I have a react native app that uses MongoDB as the database with express and node js I also use Axios to communicate with the client to the server
Now the app constantly sends and receives data from the database rapidly, e.g a user makes as much as 3 to 4 requests to and from the backend per second when the app is in use,
Everything works fine but there are a lot of 429 errors, how to handle this error or prevent it from occurring without compromising the users experiences a lot?
this below is the axios instanace
const instance = axios.create({ baseURL: 'http://9rv324283.ngrok.io' })
this below is fetching the data from the database
<NavigationEvents
onWillFocus={() => {
try {
const response = await instance.get('fetchNewDishes');
this.setState({data: response.data})
} catch(err) {
console.log(err)
}
}}>
this below is send data to the database
<TouchableOpacity onPress={() => instance.patch(`/postNewDish/${this.state.dish}`)}>
<Text style={{ fontSize: 16, color: '#555', padding: 15 }}>Post Dish</Text>
</TouchableOpacity>
I would suggest you to use axios interceptors to actually trace the error handling in axios , see below example :
import ax from 'axios';
import {config} from '../global/constant';
const baseUrl = config.apiUrl;
let axios = ax.create({
baseURL: baseUrl,
withCredentials: true,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Origin': '*',
},
});
axios.interceptors.request.use(req => handleRequest(req));
axios.interceptors.response.use(
res => handleResponse(res),
rej => handleError(rej),// here if its an error , then call handleError and do what you want to do with error.
);
// sending the error as promise.reject
const handleError = error => {
let errorResponse = {...error};
console.log({...error}, 'error');
return Promise.reject({
data: errorResponse.response.data,
code: errorResponse.response.status,
});
};
Hope it helps. feel free for doubts
Are you in control of the backend? It is possible there is a middleware that limits requests such as express-rate-limit
Make sure to either disable these middlewares, or allow many more requests per minute in the middleware configs.
I had a play around with this using https://httpstat.us/429/cors, which always returns error 429 with retry-after set to 5 (seconds), and came up with this using axios-retry:
import axios from "axios";
import axiosRetry from "axios-retry";
let instance = axios.create({ baseURL: "https://httpstat.us" });
axiosRetry(instance, {
retryCondition: (e) => {
return (
axiosRetry.isNetworkOrIdempotentRequestError(e) ||
e.response.status === 429
);
},
retryDelay: (retryCount, error) => {
if (error.response) {
const retry_after = error.response.headers["retry-after"];
if (retry_after) {
return retry_after;
}
}
// Can also just return 0 here for no delay if one isn't specified
return axiosRetry.exponentialDelay(retryCount);
}
});
// Test for error 429
instance({
url: "/429/cors",
method: "get"
})
.then((res) => {
console.log("429 res: ", res);
})
.catch((e) => {
console.log("429 e: ", e);
});
// Test to show that code isn't triggered by working API call
instance({
url: "/200/cors",
method: "get"
})
.then((res) => {
console.log("200 res: ", res);
})
.catch((e) => {
console.log("200 e: ", e);
});
I'm working on adding this to axios-retry properly for https://github.com/softonic/axios-retry/issues/72

Receving "500 Internal Server Error" on Post Request to Firebase-Cloud-Function Endpoint

I'm trying to make a POST request using axios to my firebase cloud-function on form submit in react app. But I get '500' error everytime I make a request with an html-page response This app works best with javascriot enabled.
Latest Update:
It looks like there is no issue with cloud function
code. Rather more of a react-component issue. I used Postman to send
the POST request with header prop Content-Type set to application/json
and sending body in raw format {"email": "example_email"} and got
expected response from the cloud function. But when sent the request from
react component above, I get an html file response saying the app
works best with javascript enabled
I've tried setting Content-Type to both Application/json and multipart/form-data as I suspected it to be an issue but still got no luck.
Following is my code for cloud function and react submit form:
Cloud Function
const functions = require('firebase-functions');
const cors = require('cors')({ origin: true })
const runThisFunc1 = require(./libs/runThisFunc1);
const runThisFunc2 = require(./libs/runThisFunc2);
exports.wizardFunc = functions.https.onRequest((request, response) => {
cors(request, response, () => {
let email = request.body.email;
try {
return runThisFunc1(email)
.then(data => {
console.log("Word Done by 1!");
return runThisFunc2(data);
})
.then(res => {
console.log("Word Done by 2!");
return response.status(200).send("Success");
})
.catch(err => {
console.error("Error: ", err.code);
return response.status(500).end();
});
}catch(err) {
return response.status(400).end();
}
});
});
React-Form-Component Snippet
import axios from 'axios'
...
handleSubmit = e => {
e.preventDefault()
const { email } = this.state
axios({
method: 'post',
url: `${process.env.REACT_APP_CLOUD_FUNCTION_ENDPOINT}`,
data: { email: email },
config: {
headers: {
'Content-Type': 'multipart/form-data'
}
}
})
.then(res => {
//do something with reponse here
})
.catch(error => {
console.error(error)
})
}
...
Is there something wrong I am doing in the code or the request config is wrong?

put request works for one api but not for another api

I tested with two apis for axios put.
for one api its working fine where as with another api it throws an error.
for one api its showing request as options eventhough I gave as put and I am seeing 403 forbidden error
for this api i am facing the issue 'http:///sports/sportsId',
I debugged but still I am not able to find the issue.
is it a back-end issue
can you tell me how to fix it, providing my code snippet below
savesports = () => {
console.log("savesports---->");
console.log(this.state.sports);
let savesports = this.state.sports;
savesports.updatedBy = 'xxx';
savesports.priceRuleDescription = "test description";
let data = {
name: "yyyy",
email: "sda#gmail.com",
phone: "2321313"
};
axios
.put("https://jsonplaceholder.typicode.com/users/1", data)
.then(r => console.log("dada", r));
console.log(JSON.stringify(savesports));
axios
.put(
'http:///sports/sportsId',
savesports
// { headers: { 'Content-Type': 'application/json' } }
)
.then(r => console.log(r))
.catch(e => console.log(e));
//this.toggleDrawer("right", false);
this.setState({ right: false });
this.setState({ snackBarOpen: true });
setTimeout(() => {
this.setState({ snackBarOpen: false });
}, 6000)
};
1. Check if URL is right
I would first check if the URL is right.
Try changing http:///sports/sportsId to http://sports/sportsId if that's actually the URL you are requesting to.
2. Avoid name confusion
Both the method name and the put data variable name are the same (savesports). I would change the put data variable name to something meaningful like
let sportsData = this.state.sports;
sportsData.updatedBy = 'xxx';
sportsData.priceRuleDescription = 'test description';
3. Check authentication
403 might also be to auth error. I would check if the endpoint requires any authentication token or headers.

Javascript 404 error when trying to access API call

I am trying to alter some data inside of my database, however I am getting the error once my api request is called:
Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
Along with the corresponding network error of 404. I am not quite sure why it isn't recognnizing my api call, here is the initial fetch call:
import fetch from '../../../../core/fetch/fetch.server';
import history from '../../../../core/history';
export default function checkIn(orderId) {
debugger;
return async (dispatch, getState) => {
// dispatch({ type: BOXOFFICE_CHECKING_IN });
const response = await fetch(`/api/orders/${orderId}/checkIn`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
checkedIn: true,
}),
}
);
if (response.status === 200) {
// dispatch({ type: BOOKING_CHECKED_IN });
} else {
const errorResponse = await response.json();
if (errorResponse.code === 'card_error') {
// dispatch({ type: BOXOFFICE_CHECKED_IN_ERROR });
}
}
} catch (err) {
throw err;
}
};
}
And my api file (removed everything that isn't relevant):
import { Router } from 'express';
import checkIn from '../handlers/api/orders/checkInCustomer';
export default (resources) => {
const router = new Router();
router.post('/orders/:orderId/checkIn', checkIn(resources));
return router;
};
Which ultimately is meant to call my js file that changes the data databse entry:
import { defaultTo } from 'lodash';
import guuid from '../../../../../../core/guuid';
import authenticateAdmin from '../authenticateAdmin';
import order from '../../../../client/reducers/ui/modals/order';
export default ({ knex }) =>
authenticateAdmin(knex)(async (req, res) => {
try {
console.log('checkinCustomer');
const { orderId } = req.params;
const { isCheckedIn } = req.body;
console.log(orderId);
console.log(isCheckedIn);
await knex('orders').where('is_checked_in', '=', orderId).update({ is_checked_in: isCheckedIn }).where({ id: orderId });
res.status(201).end();
} catch (err) {
console.error(err.stack || err);
}
});
Can anyone spot something that is fundamentally wrong in my code, I can assure you the file paths are all correct and all functions are visible to each other, maybe it's the way I have parsed my data?
EDIT
I thought it maybe of use to include that I am also getting a CORS error:
Access to XMLHttpRequest at 'http://localhost:3000/api/orders/c7216fc0-1197-4cb6-99d4-15760f00b6e7/checkIn' from origin 'my site name' has been blocked by CORS policy:
FURTHER EDIT
I have managed to remove the original JSON error, however I am still getting the 404 network error as well as the original CORS error... In addition to this, if I change the last section of the fetch from checkIn to say cancel which is a fully working api call, the same errors persist.
You should not use JSON.stringify() in passing data to your body.
You should pass json format in your body as you are using application/json.
I have a solution! Turns out my import fetch from '../../../../core/fetch/fetch.server';
in the initial file was wrong and should have been import fetch from '../../../../core/fetch/fetch';!

Categories

Resources