I am getting error with status 302
But while trying to log error in catch I am getting 200
post(url, data, successCallBack, errCallback) {
return this.http.post(apiDomain + url, JSON.stringify(data), {
headers: this.headers
}).catch(this.handleError).subscribe(
(res) => {
successCallBack(res.json());
},
(err) => {
errCallback(err);
}
);
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status;
console.log(error.status); // log is 200
console.log(error)
console.error(errMsg);
return Observable.throw(errMsg);
}
Requirement I want to send another post call on redirect URL redirects.
How to get Redirect URL.
Need help.
Late answer I know, but for anyone stumbling across this.
The short answer is you can't as the browser handles 302's itself and won't tell angular anything about that. What you can do is set-up an interceptor style class that monitors what is going on.
Google for angular2 http interceptor or similar, it's a little beefier than your example above and can monitor every XHR connection. An example is here:
https://www.illucit.com/blog/2016/03/angular2-http-authentication-interceptor/
What this now allows is that any connection will come through your interceptor. As we won't be able to monitor 302s, we have to think about what might happen. For example in my example the request suddenly changes the url to something with my auth in it.
Great so my 1st bit of pseudo code would be:
if (response.url.contains('my-auth string')) {
redirect....
}
I can also see on the headers provided that instead of application/json I've suddenly gone to text/html. Hmm, that's another change I can check for:
if (response.url.contains('my-auth string') && response.headers['content-type'] == 'text/html') {
redirect....
}
You may have other parameters you can check, however these were good enough to detect a redirect for me. Admittedly this is with respect to being redirected to login and not another example, hopefully you get enough distinct changes check for you to decide whether you have got a 302.
Related
My question seems pretty basic, but I came across a lot of documentation and question on this forum without getting any proper way to get the work done.
I have a secured webapp, in which I handle redirections programatically to send authentification headers with each request. Thus, instead of href links, I have buttons, which trigger the following function :
route_accessor.mjs
const access = (path = '') => {
const token = localStorage.getItem('anov_auth_token');
const dest = `http://localhost:8080/${path}`;
const headers = new Headers();
if (token) headers.append('authorization', token);
fetch(
dest,
{
method: 'GET',
headers,
mode: 'cors',
redirect: 'follow'
}
)
.then(response => {
if (response.url.includes('error/403')) {
localStorage.removeItem('anov_auth_token');
}
// Here I need to redirect to the response page
})
.catch(error => {
console.error(error);
});
};
export default access;
Then, I have NodeJs backend, which determines where I should go, either :
My requested page
A 403 page (if I sent a wrong token)
Login page (if I havent sent any token)
Backend works perfectly so far. The problem is, once I made my request, I can't display the result as I'd like. Here is what I tried. The following code takes place where I put a comment inside route_accessor.mjs.
Using window.location
window.location.href = response.url;
I tried every variant, such as window.location.replace(), but always went into the same issue : those methods launch a second request to the requested url, without sending the headers. So I end up in an infinite 403 redirection loop when token is acceptable by my server.
I tried methods listed in the following post : redirect after fetch response
Using document.write()
A second acceptable answer I found was manually updating page content and location. The following almost achieve what I want, with a few flaws :
response.text().then(htmlResponse => {
document.open();
document.write(htmlResponse);
document.close();
// This doesn't do what I want, event without the second argument
window.document.dispatchEvent(new Event("DOMContentLoaded", {
bubbles: true,
cancelable: true
}));
});
With this, I get my view updated. However, URL remains the same (I want it to change). And though every script is loaded, I have few DOMContentLoaded event to make my page fully functionnal, but they aren't triggered, at all. I can't manage to dispatch a new DOMContentLoaded properly after my document is closed.
Some other minor problems come, such as console not being cleared.
Conclusion
I am stuck with this issue for quite a time right now, and all my researches havent lead me to what I am looking for so far. Maybe I missed an important point here, but anyway...
This only concerns get requests.
Is their a proper way to make them behave like a link tag, with a single href, but with additional headers ? Can I do this only with javascript or is their a limitation to it ?
Thanks in advance for any helpful answer !
so I want to add some functionality to an already existing site, this is to make my life easier. One of the things I need that I can't seem to figure out is: how to capture the body payload data a specific outgoing "POST" request. I found the code to do it before but didn't save it and I been searching for that code for 2 days to no avail.
So here is an example of the request the site is making to server.
fetch("https://my.site/api/req", {"credentials":"include","headers":{"accept":"*/*","content-type":"application/json"},"referrerPolicy":"no-referrer-when-downgrade","body":"{\"symbol\":\"mySYM\",\"results\":[{\"data\":{\"id\":\"dataID\"},\"result\":\"signature\"}]}","method":"POST","mode":"cors"});
and the part I need to catch is the "body" portion and then unescape it so it looks like this.
{"symbol":"mySYM","results":[{"data":{"id":"dataID"},"result":"signature"}]}
Also, if possible I would like to have it only catch data when the method = POST and requests going to a specific URL, so it will catch /api/req/ and not pay attention to other URL's and/or when the method is = GET, HEAD.
Currently, I manually get the data from the request using dev tools and clicking on the correct request then scrolling down to find the POST data.
In case you need to know the reason for this. The server signs the data through the websocket connection and I am essentially trying to capture that signature to be able to replay it. I am not trying to catch the websocket data as its incomplete for my needs I need to catch the whole outgoing request body data.
Thanks in advance.
Chosen Solution:
Thanks #thirtydot for your responses. Note that my specific situation involved only fetch requests so that is the reason I went with this route. With your response, a bit of more of my own research, and the help of this post I came up with this solution. Since I don't really care to see the responses (I have other functions taking care of the responses which are important to me.).
const constantMock = window.fetch;
window.fetch = function() {
if (arguments[0] === '/api/req' && arguments[1].method === 'post'){
bodyResults(arguments[1].body)
}
return constantMock.apply(this, arguments)
}
function bodyResults(reqBody){
console.log(reqBody)
}
which put the following in console (Exactly as I wanted).
{"symbol":"NEON","results":[{"data":{"expires_at":"1561273300","id":"2469c8dd"},"signature":"6d712b9fbb22469c8dd240be13a2c261c7af0dfbe3328469eeadbf6cda00475c"}]}
except now I can return this data through that function and continue to run the rest of my script fully automated.
Extra Solution:
In case there are others struggling with similar issues and care to catch the responses of those fetch requests I could have alternatively used:
const constMock = window.fetch;
window.fetch = function() {
if (arguments[0] === '/api/req' && arguments[1].method === 'post'){
bodyResults(arguments[1].body)
}
return new Promise((resolve, reject) => {
constantMock.apply(this, arguments)
.then((response) => {
if(response.url.indexOf("/me") > -1 && response.type != "cors"){
console.log(response);
// do something for specificconditions
}
resolve(response);
})
.catch((error) => {
reject(response);
})
});
}
function bodyResults(reqBody){
console.log(reqBody)
}
Possible XHR Solution
NOTE: this one is untested! An alternative Solution for XHR requests could be done similarly using something along the lines of:
(function(open) {
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
alert('Intercept');
open.call(this, method, url+".ua", async, user, pass);
};
})(XMLHttpRequest.prototype.open);
Hope this helps!
So I came across this today and I'm not quite sure if its exactly what you've been looking for over 2 years ago, but solved my problem and I thought I should share it if others needed.
I'm currently using a marketing automation tool which is quite limiting when it comes to landing pages, but I wanted the client to be able to update the content whenever needed and still have access to custom functionality, so I needed the payload which was being sent by the form submission.
Here is what I used to get the form submission payload:
(function() {
var origOpen = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
console.log('request started!');
console.log(arguments[0]);
this.addEventListener('load', function() {
console.log('request completed!');
console.log(this.status);
});
origOpen.apply(this, arguments);
};
})();
The arguments[0] piece is actually the JSON sent as the payload, and the status code is the response (200), stating the request was successfull.
I partially used code from this other response here: https://stackoverflow.com/a/27363569/1576797
I'll start by saying I'm a bit of a newb when it comes to Javascript/React. I am attempting to communicate with my WCF endpoint server but I can’t seem to send any POST messages without getting a response:
OPTIONS http://###/testbuyTicket 405 (Method Not Allowed)
It seems that because I am sending it with content-type JSON it requires a ‘pre-flight’ and this is where it is failing.
This is my client code:
var headers = {
'headers': {
'Content-Type': 'application/json',
}
}
axios.post(call, data, headers).then(res => {
try {
if (res) {}
else {
console.log(res);
}
}
catch (err) {
console.log(err);
}
}).catch(function (error) {
console.log(error);
});
Here is the error details:
I don’t see why this pre-flight is failing. On the server I have already allowed everything I believe I need:
{"Access-Control-Allow-Origin", "*"},
{"Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS"},
{"Access-Control-Allow-Headers", "X-PINGOTHER,X-Requested-With,Accept,Content-Type"}
[ServiceContract]
public interface IPlatform
{
[OperationContract]
[WebInvoke(UriTemplate = "testbuyTicket")]
TicketResponse TestBuyTicket(PurchaseRequest purchaseRequest);
}
Any help would be appreciated. I feel like I've tried everything. Thanks in adance.
I have found a solution, I'm not sure if it's the most elegant solution but it does work.
Basically I have an endpoint that the call should be directed too, but it only accepts POST requests, so I have added an OPTIONS endpoint with an empty method and it all appears to work now.
So I now have:
[ServiceContract]
public interface IPlatform
{
[OperationContract]
[WebInvoke(UriTemplate = "testbuyTicket")]
TicketResponse TestBuyTicket(PurchaseRequest purchaseRequest);
[OperationContract]
[WebInvoke(UriTemplate = "testbuyTicket", Method = "OPTIONS")]
TicketResponse TestBuyTicketOptions(PurchaseRequest purchaseRequest);
}
Doing this allows the server to respond to the OPTIONS call and then the POST call.
Thanks everyone for your assistance.
Big shoutout to #demas for the idea, see post Response for preflight has invalid HTTP status code 405 for more info
Like #charlietfl says, this doesn't appear to be a CORS issue, since you seem to be returning the headers OK (per the screenshot).
My guess is that your web server (Apache or whatever) doesn't allow OPTIONS requests - many only allow GET/POST/HEAD by default.
Probably a simple web server setting...
I want to fetch some data from a server via axios in my react project. When i put the url on browser and hit enter browser ask me username and password and after that, i can see the json data. But i dont know how to set the password and username in axios header in a get method. I have searched it in many forums and pages,especially this link didin't help me: Sending axios get request with authorization header . So finally i tried (many things before this, but i was more confused):
componentDidMount() {
axios.get('http://my_url/api/stb', {auth: {
username: 'usrnm',
password: 'pswrd'
}})
.then(function(response) {
console.log(response.data);
console.log(response.headers['Authorization']);
}).catch(err => console.log(err));
}
And i can not get anything. I get this error in console:
Error: Network Error
Stack trace:
createError#http://localhost:3000/static/js/bundle.js:2195:15
handleError#http://localhost:3000/static/js/bundle.js:1724:14
Actually, the api documentation mentioned that with these words:
If there is no header or not correct data - server's answer will
contain HTTP status 401 Unauthorized and message:
< {"status":"ERROR","results":"","error":"401 Unauthorized request"}
For successful authentification is sufficient to add in every request
header to the API:
Authorization: Basic <base64encode("login":"password")>
The weird thing is, when i use postman, the response send me a "401 unauthorized" response below the body section. But i can not see any 401 errors in browser's console.
Ok i found the solution. As i mentioned in the comments that i wrote for my question, there was a cors problem also. And i figured out that cors problem was appearing because of that i can not authorize correctly. So cors is a nature result of my question. Whatever.. I want to share my solution and i hope it helps another people because i couldent find a clear authorization example with react and axios.
I installed base-64 library via npm and:
componentDidMount() {
const tok = 'my_username:my_password';
const hash = Base64.encode(tok);
const Basic = 'Basic ' + hash;
axios.get('http://my_url/api/stb', {headers : { 'Authorization' : Basic }})
.then(function(response) {
console.log(response.data);
console.log(response.headers['Authorization']);
}).catch(err => console.log(err));
}
And dont forget to get Authorization in single quotes and dont struggle for hours like me :)
An AngularJS version 1.4.8 app is getting an unhandled 403 error when its login form sends data to a backend REST authentication service after the user's browser has been left open for many (16 in this case) hours. Upon deeper analysis, the root cause is that the client AngularJS app has outdated cookies for XSRF-TOKEN and JSESSIONID, which causes the backend Spring Security to reject the request to the public /login1 service because Spring thinks the request is cross site request forgery.
The problem can be resolved manually if the user closes all browser windows and then re-opens a new browser window before making the request again. But this is not an acceptable user experience. I have read the AngularJS documentation at this link, and I see that I can add an errorCallback function, but how specifically should i re-write the function to handle the 403 error?
Here is the original this.logForm() method in the authorization service, which you can see does not handle 403 errors:
this.logForm = function(isValid) {
if (isValid) {
var usercredentials = {type:"resultmessage", name: this.credentials.username, encpwd: this.credentials.password };
$http.post('/login1', usercredentials)
.then(
function(response, $cookies) {
if(response.data.content=='login1success'){// do some stuff
} else {// do other stuff
}
}
);
}
};
Here is my very rough attempt at a revised version of the this.logForm() method attempting to handle a 403 error following the example in the AngularJS documentation:
this.logForm = function(isValid) {
if (isValid) {
var usercredentials = {type:"resultmessage", name: this.credentials.username, encpwd: this.credentials.password };
$http({ method: 'POST', url: '/login1', usercredentials })
.then(
function successCallback(response, $cookies) {
// this callback will be called asynchronously when the response is available
if(response.data.content=='login1success'){// do some stuff
} else {// do other stuff
}
},
function errorCallback(response, status) {// is status a valid parameter to place here to get the error code?
// called asynchronously if an error occurs or server returns response with an error status.
if(status == 403){
this.clearCookies();
// try to call this POST method again, but how? And how avoid infinite loop?
}
}
);
}
};
What specific changes need to be made to the code above to handle the 403 error due to server-perceived XSRF-TOKEN and JSESSIONID issues? And how can the post be called a second time after deleting the cookies without leading to an infinite loop in the case where deleting the cookies does not resolve the 403 error?
I am also looking into global approaches to error handling, but there is a combination of public and secure backend REST services, which would need to be handled separately, leading to complexity. This login form is the first point of user entry, and I want to handle it separately before looking at global approaches which would retain a separate handling of the login form using methods developed in reply to this OP.
You could restructure your http calls to auto retry, and use promises in your controllers (or whatever)
var httpPostRetry = function(url, usercredentials) {
var promise = new Promise(function(resolve, reject) {
var retries = 0;
var postRetry = function(url, usercredentials) {
if (retries < 3) {
$http({ method: 'POST', url: '/login1', usercredentials })
.then(function(result) {
resolve(result);
}).catch(function(result) {
retries ++;
postRetry(url, usercredentials);
});
} else {
reject(result);
}
};
}.bind(this));
return promise;
}
and then you would call
httpPostRetry(bla, bla).then(function(result) {
// one of the 3 tries must of succeeded
}).catch(function(result) {
// tried 3 times and failed each time
});
To handle specific http errors you can broadcast that specific error and handle that case in a specific controller. Or use a service to encapsulate the status and have some other part of your code handle the UI flow for that error.
$rootScope.$broadcast('unauthorized http error', { somedata: {} });
Does this help?
Have a look at the angular-http-auth module and how things are done there. I think one key element you would want to use is a http interceptor.
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests. The interceptors leverage the promise APIs to fulfill this
need for both synchronous and asynchronous pre-processing.
After playing around with interceptors you can look at the angular-http-auth http buffer and the way they handle rejected requests there. If their interceptor receives a responseError, they add the config object - which basically stores all information about your request - to a buffer, and then any time they want they can manipulate elements in that buffer. You could easily adept their code to manipulate the config's xsrfHeaderName, xsrfCookieName, or parameters on your behalf when you receive a 403.
I hope that helps a little.