I am using superagent 3.6.2, in a react project and keep getting:
Error: Request has been terminated
superagent/lib/client in crossDomainError
None of the users face any issue but my error logs are filled with these.
I make requests like this:
_pendingRequests[someKey] = req
.set('Authorization', `Bearer XYZ`)
.withCredentials()
.end(handleRes(someKey, options));
}
and if the same request is hit twice I cancel the previous one:
_pendingRequests[someKey]._callback = () => {};
_pendingRequests[someKey].abort();
_pendingRequests[someKey] = null;
Is there a reason why this is happening? Is it because of slow network on those devices or is it because of I am aborting a pending request?
Related
I've had csrf protection with the csurf module working for a while now on my React SPA. I am also using passport for authentication. I do not do any server-side rendering, so the server sends a csrf token in the response body to the client when it hits the /users/current endpoint, which is protected with csrfProtection, something like this:
import csrf from 'csurf';
const csrfProtection = csrf();
router.get("users/current", csrfProtection, async function(req, res)
{
.....
res.write(JSON.stringify({ ..., csrfToken: req.csrfToken() }));
res.end();
}
On the client side I then add the token to all subsequent request headers, a bit like this:
axiosInstance.get("/users/current")
.then(resJson =>
{
axiosInstance.interceptors.request.use(config =>
{
config.headers["x-csrf-token"] = resJson.data.csrfToken;
return config;
});
}
My first question is how the first request even manages to pass the csrfProtection without a token in its header. Yet since the token can only be accessed on the server to send to the client if the route is csrf protected, I don't see a way around this, and it does work somehow.
However, recently I have been getting "ForbiddenError: invalid csrf token" when a user logs in or deletes their account. This has only started happening after I upgraded all my node packages to the latest versions. First the client makes a request to /users/login to submit the username & password, and then makes a request to /users/current to get the new csrf token:
axiosInstance.post("/users/login", {
"username": login.username,
"password": login.password
})
.then(async resJson =>
{
// *code to update user details in redux store*
// ......
axiosInstance.interceptors.request.use(config =>
{
config.headers["x-csrf-token"] = undefined;
return config;
});
return resJson;
})
.then(async resJson =>
{
const { csrfToken } = await axiosInstance.get("/users/current")
.then(resJson => resJson.data);
axiosInstance.interceptors.request.use(config =>
{
config.headers["x-csrf-token"] = csrfToken;
return config;
});
return resJson.data;
}
I suspect it's something to do with subsequent requests coming from a different userId (which I obtain from req.user[0].userId), with which csurf will not accept the previously issued token. But I have no idea how to issue the new token csurf does expect, to the client. And it still doesn't explain why what I had before has suddenly stopped working since none of my logic has changed. This isn't the kind of error I'd typically expect after package updates.
Here someone mentions you can just set any header on the client and have the server check for that. atm I am adding the csrf token to all the client's request headers and using the csurf module's request handler function to check it, but there is nothing stopping me from writing my own. If this is true, the value of the header doesn't even matter, just that it exists. I am holding off on this option though because I feel there is something basic I'm not understanding about my current setup, which once rectified will mean this can be easily fixed.
Would appreciate any help or explanation! Thanks 🤍
I am trying to add RETRY Logic in the context of - I make an API call -> response is 401 -> I invoke APi to request for a NEW Token in the background. The poin there si MY API Calls shouldnt fail. Following is my API File (This is common - Every API in my application invokes this File to make an FETCH)
NOTE : I have seen articles using the fetch().then() approach, but we are using YIELD.
Specific API File -
// apiRequest = part of api.js file i am specifying below
const response = yield retry(3,1000,apiRequest,options); // My apiRequest while trying for getting new access tokens send me a NULL, do we want that ?
if (undefined !== response && null !== response) {
const formattedResponse = yield apply(response, response.json);
if (response.status === 200) {
yield call(handleAddCampaignResponseSuccess, formattedResponse);
} else {
yield call(handleAddCampaignResponseFailure, formattedResponse);
}
} else{
// Show some Message on UI or redirect to logout
}
// api.js
function* apiRequest(options) {
const { method, body, url } = options;
const accessToken = yield select(selectors.AccessToken);
const idToken = yield select(selectors.IdToken);
try {
var response = yield call(fetch, url, {
method: method,
body: body,
headers: {
"Content-Type": ContentTypes.JSON,
Authorization:
accessToken != "" ? `Bearer ${accessToken} ${idToken}` : "",
},
});
if (null !== response) {
if (response.status === HTTP_CODES.HTTP_UNAUTHORIZED) {
// Unauthorized requests - redirect to LOGOUT
// Request for Refresh Token !
yield put(refreshTokenOnExpiry());
return null; // Is this necessary
} else if (response.status === HTTP_CODES.HTTP_NOT_FOUND) {
return null;
} else if (response.status === HTTP_CODES.HTTP_SERVER_ERROR) {
// Logout cos of serrver error
yield put(handleLogout());
return null;
} else {
console.log("From Else part");
// - Called on intent to ensure we have RESET redirections and that it does not cause issues of redirection.
yield put(resetRedirections());
return response;
}
} else {
// Handle Logout
yield put(stopTransition());
yield put(handleLogout());
}
} catch (error) {
// Cors Error in case of DEV URL
// See if SAGA is Still listening to the Action Dispatches
console.log("From CATCH BLOCK");
yield put(stopTransition());
yield put(handleLogout());
return null;
}
}
My concern is the documentation says that - if API request fails then it will retry, I do not get the meaning of it. Does it mean if the API returns NULL, or anything other than Http 200 ? Cos I want the API to retry in case of 401
API.JS is the file invoked by ALL API's across my website. Also, how can I ensure that refreshTokenOnExpiry gets called ONLY once (meaning at a time there will be multiple API calls and each one when got a 401 will eventually invoke refreshTokenOnExpiry this API)
I am new to generator functions, so I am sure I must have goofed up somewhere.
Also if anyone who can help me build this code correctly, would be great help. Thanks !
Adding Image for reference - I want the FAILED API's to be retried which aint happening :
My concern is the documentation says that - if API request fails then it will retry, I do not get the meaning of it. Does it mean if the API returns NULL, or anything other than Http 200 ? Cos I want the API to retry in case of 401
Scroll down to the section "Retrying XHR calls" in the redux-saga recipes to get an idea of what the retry effect is doing behind the scenes.
The retry effect can be used on any function, no just an API call, so it's not looking at the response code. It defines "failure" as code that throws an error rather than completing execution. So what you need to do is throw an error in you apiRequest.
No guarantees, but try this:
if (response.status === HTTP_CODES.HTTP_UNAUTHORIZED) {
// Unauthorized requests - redirect to LOGOUT
// Request for Refresh Token !
yield put(refreshTokenOnExpiry());
throw new Error("invalid token");
}
You need to figure out how to make sure than the new token gets set before retrying. You might want to build your own chain of actions rather than relying on retry. For example, you can put an action with type "RETRY_WITH_NEW_TOKEN" that has a payload containing the original options and the token that it was tried with. That way you can compare it against the token in state to see if you have a new one.
Premise / What you want to achieve
React x Redux (port: 3000)
Go (port: 8080)
I am making a SPA.
I run into a CROS error when hitting the Go API.
I've encountered this problem many times, and every time I think it's solved, I hit a new API.
I should have made the basic settings, but I'm in trouble because I don't know what caused it.
We would appreciate it if you could help us.
Problem / Error message
Access to XMLHttpRequest at'http://localhost:8080/login' from origin'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No'Access-Control -Allow-Origin'header is present on the requested resource.
I encountered this when I hit the login API (post).
However, when I encountered this problem several times, I set cros on the header of api and axios side, and
Another get API avoided the error.
Also, when you hit api with postman, it becomes header
We have also confirmed that the header set in Go such as Allow-Origin is given without any problem.
Applicable source code
Header settings in Go
w.Header (). Set ("Content-Type", "application /json")
w.Header (). Set ("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header (). Set ("Access-Control-Allow-Credentials", "true")
react axios settings
axios.defaults.baseURL ='http://localhost:8080';
axios.defaults.headers.post ['Content-Type'] ='application/json';
Posting code with an error
export const signIn = (email, password) => {
return async (dispatch) => {
try {
const response = await axios.post ('/login', {
email: email,
password: password,
});
const data = response.data;
dispatch (
signInAction ({
isSignedIn: true,
})
);
} catch (error) {
console.log (error);
}
};
};
Code hitting a successful getapi
useEffect (() => {
async function fetchTickers () {
try {
const response = await axios.get (`/ticker?Symbol=^skew`);
const data = response.data;
setChartAry ([... chartAry, [... data.daily]]);
} catch (error) {
console.log (error);
setChartAry ([]);
}
}
fetchTickers ();
}, []);
What I tried
I tried all the solutions that hit with stackoverflow etc. Also, considering the possibility of a problem with the browser itself, we also cleared the cache.
Is it the difference between axios by get and post? And how should I debug it?
I had this problem some time ago but I used Express for the backend, who knows this can solve your problem too.
try adding this to the axios settings axios.defaults.withCredentials = true;
You also need to allow the OPTIONS method for preflight requests
this article might help you solve the CORS problem on the backend: https://flaviocopes.com/golang-enable-cors/
The method was validated in gorilla / mux.
- r.HandleFunc("/login", app.loginHandler).Methods("POST")
+ r.HandleFunc("/login", app.loginHandler).Methods("POST", "OPTIONS")
We also had to deal with preflight.
if r.Method == "OPTIONS" {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.WriteHeader(http.StatusOK)
return
}
In my React app, I'm using fetch() to get data from my API, _callAPI() function gets domain parameter and call API if a website of the domain exists in my DB. If it exists, it returns the website's object, otherwise it returns 500. So, I can't figure out if the website exists until I use fetch(). The problem is that every time fetch() doesn't find anything, it throws the following:
container.jsx:25 GET
http://localhost:3000/boutiques/detail/?q=testdomain.com
500 (Internal Server Error)
When it doesn't doesn't find a lot of websites, the console log is packed with that error message. Is there a way to ignore that sort of message while fetching?
fetch()
_callApi = () => {
const { domain } = this.props;
return fetch(`/boutiques/detail/?q=${domain}`)
.then(response => {
if (response.status === 500) {
return 500;
}
return response.json();
})
.then(json => json)
.catch(err => console.log(err));
};
If you want to mute the browser error:
Unfortunately, this cannot be done, as this type of message in the
console is printed by chrome itself. Repressing this type of message
has been debated for years, but the consensus seems to be that this
message is desirable
Credits.
If you want to mute the Unhandled error in the console:
You can always mute the error on front-end, as follows:
.catch(err => { const mute = err })
But it would be better to notify somehow the user about the error and not doing such workarounds.
Also it would better your server to return an error message in the response and on the front-end side you will proceed it.
Looking into your case, it may be better the server to response with status code 400. Here are the HTTP error codes and their purpose:
4xx (Client Error): The request contains bad syntax or cannot be
fulfilled
5xx (Server Error): The server failed to fulfill an
apparently valid request
I am trying to return user data from a login with Polymer. I have it working with Postman, but am having trouble translating it into Polymer.
In Postman this returns a JSON object, but in Polymer it is returning undefined.
Polymer Client Code [Connecting to node.js server]
<iron-ajax id="ajaxUser"
url="http://localhost:8080/login"
method="post"
handle-as="json"
content-type="application/json"
headers='{"Access-Control-Allow-Origin": "*"}'
params="[[params]]"
on-response="saveUserCredentials"
last-response="{{user}}"></iron-ajax>
...
<paper-input id="username"></paper-input>
<paper-input id="password"></paper-input>
<paper-button on-tap="loginUser"></paper-button>
...
loginUser() {
this.params = {"username": this.$.username.value, "password": this.$.password.value};
console.log(this.params); // logs this.params as populated JSON
let request = this.$.ajaxUser.generateRequest();
request.completes.then(req => {
console.log(req); // logs <iron-request></iron-request>
console.log(this.user); // logs []
})
.catch(rejected => {
console.log(rejected.request); // not returned
console.log(rejected.error); // not returned
})
}
saveUserCredentials() {
console.log(this.user);
}
Node, Express, mssql Server Code [Connecting to SQL Server database]
app.post("/login", (req, res) => {
session.login(req, res)
})
...
exports.login = (req, res) => {
sql.connect(config.properties)
.then(pool => {
pool.request()
.input('user', sql.VarChar(50), req.body.username)
.input('password', sql.VarChar(50), req.body.password)
.query("SELECT role FROM Login WHERE username = #user AND password = #password")
.then(response => res.send(response))
.catch(err => res.send(err))
})
}
Error
SyntaxError: Unexpected token # in JSON at position 0 .
at JSON.parse ()
at createStrictSyntaxError (C:\node_modules\body-parser\lib\types\json.js:157:10)
at parse (C:\node_modules\body-parser\lib\types\json.js:83:15)
at C:\node_modules\body-parser\lib\read.js:121:18 .
at invokeCallback (C:\node_modules\raw-body\index.js:224:16)
at done (C:\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (C:\node_modules\raw-body\index.js:273:7)
at IncomingMessage.emit (events.js:159:13)
at endReadableNT (_stream_readable.js:1062:12)
at process._tickCallback (internal/process/next_tick.js:152:19)
The first issue appears to be that your server is expecting a JSON object in the request, but the server sees the request as a string due to a missing Content-Type header on your request. To set the Content-Type for JSON requests, set <iron-ajax>.contentType to application/json:
<iron-ajax content-type="application/json" ...>
OK, I was able to get a server response by setting content-type=application/json as an iron-ajax property. Am now getting Unexpected token # in JSON at position 0 as a server side error...
That sounds like the request is not actually valid JSON (since it contains a # as the first character). Use the Chrome DevTools Network Panel to inspect the actual contents of the payload. I wouldn't rely solely on console.log in your code.
Also, it is now making two requests when I submit. One with the content-type set, which resolves to 200, and one with that resolves to 400 with parsing error.
The first message is likely the preflight request, which is sent (as part of CORS) to the server to check whether content-type="application/json" is an allowed header. The second message is the intended data request, but that fails with the following error.
And a client side error of No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4001' is therefore not allowed access. The response had HTTP status code 400.
Your server needs to enable CORS requests. There are various ways to accomplish this, but the simplest Node solution might be to use the cors package:
const cors = require('cors');
const app = express();
app.use(cors());