error.response is undefined if axios request fails - axios

I cannot access the error (response) status code if an axios request has failed in my Vue.js app. I cannot figure out why the response is undefined in both '.catch' and 'axios.interceptors.response'. I followed this instruction that demonstrates that 'error.response' can be easily accessed with a code like this:
axios.interceptors.response.use(
(response) => {
console.log(response);
return response;
},
(error) => {
handleApiFail(error.response);
});
If I add this code to 'main.js' in my app, 'handleApiFail' is called when a request fails, but error.response is undefined in the second lambda and the first lambda is not called. If a request succeeded the 'response' in the first lambda is defined and has the status code.
EDIT1: this is not an option because my OPTIONS requests do not require authorization. Also there are various posts describing the same situation.

The lack of
access-control-allow-origin: *
header in the response caused the browser to block my request.
Adding the header makes axios work fine.

I have code like this:
axios.interceptors.response.use(null, (error) => {
.........
return Promise.reject();
});
Then, I found I miss to return my "error" in promise reject, correct like this:
return Promise.reject(error);

This is an idiosyncrasy of axios. A quick solution to this is to serialize the response:
JSON.stringify(error)
Please refer to this GitHub issue for more info: https://github.com/axios/axios/issues/960
As someone pointed out there, you can check the error status code in the action and run some other commit depending on it.

Related

Unable to make HTTP Post from custom Karma reporter

I need to publish my Karma test results to a custom REST API. To handle this automatically, I've written a custom Karma reporter. I'm trying to use the run_complete event so that the POST happens after all browsers finish. However, no HTTP call is being made.
I'm using Axios 0.19.2 to do the actual HTTP call, but the same thing happens with node-fetch. The tests are being run by the Angular cli via ng test. My Karma config is lengthy but other than having a million different reporters and possible browser configs, is pretty much standard.
This is my onRunComplete method:
self.onRunComplete = function () {
var report = ... ; // logic to generate a JSON object, not relevant
var url = '...'; // the endpoint for the request
try {
console.log('Sending report to ' + url);
axios.post(url, report, {headers: {'Content-Type': 'application/json'}})
.then(function(response) {
console.log('Success!');
console.log(response);
})
.catch(function(error) {
console.log('Failure!');
console.log(error);
});
} catch (err) {
console.log('Error!');
console.log(err);
}
}
At the end of the test run, it writes to console the 'Sending report to...' message, and then immediately ends. The server does not receive the request at all.
I also tried adding explicit blocking using a 'inProgress' boolean flag and while-loop, but that pretty much just leaves the entire test run hanging since it never completes. (Since the request is never made, the 'inProgress' flag is always true and we never hit the then/catch promise handlers or the catch block.)
I have verified that the Axios POST request works by taking the entire contents of the onRunComplete as shown here, putting it in its own JS file, and calling it directly. The report logs as expected. It's only when I call from inside of Karma that it's somehow blocked.
Since Karma's documentation pretty much boils down to "go read how other people did similar things!" I'm having trouble figuring out how to get this to work. Is there a trick to getting an HTTP request to happen inside of a custom reporter? Why does my implementation not work?
Looks like the post request is made asynchronously - that is the request is made and control resumes almost immediately to the method which completes... try instead:
self.onRunComplete = function () {
var report = ... ; // logic to generate a JSON object, not relevant
var url = '...'; // the endpoint for the request
try {
console.log('Sending report to ' + url);
await axios.post(url, report, {headers: {'Content-Type': 'application/json'}})
...
}
}

Atlassian Jira - 401 only when using query parameters

Currently working on a JIRA addon using the ACE framework. Executing a request using the integrated httpClient.
When I make a request such as this
https://instance.atlassian.net/rest/api/3/search
it works fine using the header Authorization: JWT <token> but when I run the same request with a query parameter like this
https://instance.atlassian.net/rest/api/3/search?maxResults=1
the request fails with a 401. I have confirmed that the JWT is not expired due to reverting the query parameters and seeing success again.
My atlassian-connect.json has scope READ as requested by the endpoint.
Any suggestions?
I was surprised that the rest call "rest/api/2/search?maxResults=1" worked. But it did when I was logged into my instance.
If I try that as JQL in Issue Search (maxResults=1), I get an invalid or unauthorized error message.
My instance is on premise (API V2). Yours appears to be in the cloud (V3). So it may be that the REST search works more like the Issue Search in V3 and is therefore returning the 401
It's a guess that should be easy to test... replace your maxResults=1 with some actual JQL or a filter ID and see if your results change
Since you're using ACE and utilizing httpClient, you might want to try the checkValidToken() route instead. The snippet below worked for me.
app.get('/mySearch', addon.checkValidToken(), function(req, res) {
var httpClient = addon.httpClient(req);
httpClient.get({
url: '/rest/api/3/search?maxResults=1',
headers: {
'X-Atlassian-Token': 'nocheck',
'Content-Type': 'application/json'
}
},
function (err, httpResponse, body) {
if (err) {
return console.error('Search failed:', err);
}
console.log('Search successful:', body);
});
});

Angular 2 REST request HTTP status code 401 changes to 0

I'm developing an Angular2 application. It seems when my access-token expires the 401 HTTP Status code gets changed to a value of 0 in the Response object. I'm receiving 401 Unauthorized yet the ERROR Response object has a status of 0. This is preventing me from trapping a 401 error and attempting to refresh the token. What's causing the 401 HTTP status code to be changed into HTTP status code of 0?
Here's screenshot from Firefox's console:
Here's my code:
get(url: string, options?: RequestOptionsArgs): Observable<any>
{
//console.log('GET REQUEST...', url);
return super.get(url, options)
.catch((err: Response): any =>
{
console.log('************* ERROR Response', err);
if (err.status === 400 || err.status === 422)
{
return Observable.throw(err);
}
//NOT AUTHENTICATED
else if (err.status === 401)
{
this.authConfig.DeleteToken();
return Observable.throw(err);
}
else
{
// this.errorService.notifyError(err);
// return Observable.empty();
return Observable.throw(err);
}
})
// .retryWhen(error => error.delay(500))
// .timeout(2000, new Error('delay exceeded'))
.finally(() =>
{
//console.log('After the request...');
});
}
This code resides in a custom http service that extends Angular2's HTTP so I can intercept errors in a single location.
In Google Chrome, I get this error message:
XMLHttpRequest cannot load https://api.cloudcms.com/repositories/XXXXXXXXXXXXXXXX/branches/XXXXXXXXXXXXXXXX/nodesXXXXXXXXXXXXXXXX. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://screwtopmedia.local.solutiaconsulting.com' is therefore not allowed access. The response had HTTP status code 401.
This is confusing because I am including 'Access-Control-Allow-Origin' header in request.
Here's a picture of results received in Google Chrome:
I've tried accessing 'WWW-Authenticate' Response Header as a means to trap for 401. However, the following code returns a NULL:
err.headers.get("WWW-Authenticate")
It's puzzling that I'm getting a CORS issue because I'm not getting any CORS errors when a valid access token is provided.
How do I trap for 401 HTTP status code? Why is 401 HTTP status code being changed to 0?
Thanks for your help.
The issue is related to CORS requests, see this github issue
No 'Access-Control-Allow-Origin' header is present on the requested
resource
means that 'Access-Control-Allow-Origin' is required in the response headers.
Angular is not getting any status codes, that's why it gives you a 0 which is caused by browser not allowing the xml parser to parse the response due to invalid headers.
You need to append correct CORS headers to your error response as well as success.
If cloudcms.com do to not set the Access-Control-Allow-Origin in 401 response, then nothing much you can do. You properly have to open support ticket with them to confirm if that is normal behavior.
javascript in browsers(FF, Chrome & Safari) I tested won't receive any info if CORS error occur, other than status 0. Angular2 has no control of it.
I created a simple test and get the same result as yours:
geturl() {
console.log('geturl() clicked');
let headers = new Headers({ 'Content-Type': 'application/xhtml+xml' });
let options = new RequestOptions({ 'headers': headers });
this.http.get(this.url)
.catch((error): any => {
console.log('************* ERROR Response', error);
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Web Server error';
return Observable.throw(error);
})
.subscribe(
(i: Response) => { console.log(i); },
(e: any) => { console.log(e); }
);
}
After much googling, I found the following(https://github.com/angular/angular.js/issues/3336):
lucassp commented on Aug 19, 2013
I found the issue for a while now but I forgot post here a reply. EVERY response, even Error 500, must have the CORS headers attached. If the server doesn't attach the CORS headers to the Error 500 response then the XHR Object won't parse it, thus the XHR Object won't have any response body, status, or any other response data inside.
Result from Firefox network monitor seems supporting the above reason.
Javascript request will receive empty response.
Plain url request(copy&paste the link in the address bar) will get response body
Javascript use XHRHttpRequest for http communication.
When a XHR reply arrive, the browser will process the header, that's why you will see those 401 network messages. However, if the XHR reply is from a different host then the javascript AND the response header contain no CORS header(eg Access-Control-Allow-Origin: *), the browser will not pass any info back to the XHR layer. As a result, the reply body will be completely empty with no status(0).
I tested with FF 48.0.1, Chrome 52.0.2743.116 and Safari 9.1.2, and they all have the same behavior.
Use browser network monitor to check response header for those 401 Unauthorized entries, it is very likely that there is no Access-Control-Allow-Origin header and causing the issue you are facing. This can be a bug or by design on the service provider side.
Access-Control-Allow-Origin is a response only http header. For more information of https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#The_HTTP_response_headers
I work for Cloud CMS. I can confirm that we properly set CORS headers on API calls. However, for 401 responses the headers are not getting set properly. This has been resolved and the fix will be available on the public API at the end of this week.
BTW if you use our javascript driver https://github.com/gitana/gitana-javascript-driver instead of writing to the API directly then the re-auth flow is handled automatically and you do not need to trap 401 errors at all.
Stumbled upon this item:
What happens in my case was that:
I was requesting REST from different domain
The resource returned 401, but there was no "Access-Control-Allow-Origin" set in the error response.
Chrome (or your browser) received 401 and showed in network tab (See below count : 401), but never passed to Angular
since there was no CORS allowed (missing "Access-Control-Allow-Origin" header) - Chrome could not pass this response into Angular. This way I could see 401 in the Chrome but not in Angular.
Solution was simple (extends on TimothyBrake's answer) - to add the missing header into the error response. In Spring boot I put: response.addHeader("Access-Control-Allow-Origin", "*"); and I was sorted out.
#Component
public class JwtUnauthorizedResponseAuthenticationEntryPoint
implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"You would need to provide the Jwt Token to Access This resource");
}
}
PS: Make sure the bean is provided into HttpSecurity config in your WebSecurityConfigurerAdapter. Refer to: https://www.baeldung.com/spring-security-basic-authentication if in doubt.
Hope this helps.
According to W3C, if the status attribute is set to 0, it means that:
The state is UNSENT or OPENED.
or
The error flag is set.
I think that this isn't an Angular2 issue, it seems like your request has a problem.
I had the same issue and was due to the server not sending the correct response (even though the console log stated a 401 the Angular error had status 0). My server was Tomcat with Java application using Spring MVC and Spring Security.
It is now working using folowing setup:
SecurityConfig.java
...
protected void configure(HttpSecurity http) throws Exception {
....
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
// allow CORS option calls
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
...
SomeAuthenticationEntryPoint.java
#Component
public class SomeAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
HttpStatus responseStatus = HttpStatus.UNAUTHORIZED;
response.sendError(responseStatus.value(), responseStatus.getReasonPhrase());
}
}
web.xml
<error-page>
<error-code>403</error-code>
<location>/errors/unauthorised</location>
</error-page>
<error-page>
<error-code>401</error-code>
<location>/errors/unauthorised</location>
</error-page>
Controller to handle the /errors/... previously defined
#RestController
#RequestMapping("/errors")
public class SecurityExceptionController {
#RequestMapping("forbidden")
public void resourceNotFound() throws Exception {
// Intentionally empty
}
#RequestMapping("unauthorised")
public void unAuthorised() throws Exception {
// Intentionally empty
}
}
}
You should use a 3rd party http protocol monitor (like CharlesProxy) rather than Chrome dev tools to confirm which headers are actually being sent to the API service and if it is returning a 401.

FB API: Resource interpreted as Script but transferred with MIME type text/html

I'm using FBJS to post photo
FB.api('me/photos', 'post', {
message: 'some message',
url: 'some url'
}, function(response){
if (!response || response.error) {
/*some error alert*/
else {
/*some success*/
}
}
});
Almost, it works fine, but OCCASIONALLY, it shows error alert.
I see on Console, it says: Resource interpreted as Script but transferred with MIME type text/html
How to deal with it?
I've tried some solutions (such as Content-Type, script type...) but it still does not works in SOME times :(
Please help me.
I had that problem and in my case the reason was local - the "Disconnect" Chrome addon, that was blocking requests to facebook, I just forgot that I had it installed.
Have you tried
headers: [
{ "name":"Content-Type",
"value":"text/javascript; charset=UTF-8"}
]
If it is only occasional, maybe you're hitting an API call limit and it's returning an error? Check that the data is correct, even if it is getting an error. Use fiddler to get more clear diagnostic results.

Origin is not allowed by Access-Control-Allow-Origin for HTTP DELETE

I currently playing around with the Facebook JavaScript SDK and the Scores API ( https://developers.facebook.com/docs/score/ ). I wrote a small application to save (post) scores and now I want to delete scores. Posting (saving) them works fine.
My code looks like this:
var deleteHighScoreUrl = 'https://graph.facebook.com/'+facebook.user.id+'/scores?access_token='+facebook.application.id+'|'+facebook.application.secret;
jQuery.ajax(
{
type: 'DELETE',
async: false,
url: deleteHighScoreUrl,
success: function(data, textStatus, jqXHR)
{
console.log('Score deleted.');
}
});
The "facebook" variable is an object that holds my application data. For HTTP POST it works fine but for HTTP DELETE I get the response "NetworkError: 400 Bad Request" in Firebug (with Firefox 10). I saw that Firefox first sends an HTTP OPTIONS (to see if it is allowed to use HTTP DELETE) which leads to this error so I tried the same thing with Google Chrome. Google Chrome sends a real HTTP DELETE which then returns:
"XMLHttpRequest cannot load
https://graph.facebook.com/USER_ID/scores?access_token=APP_ID|APP_SECRET.
Origin MY_DOMAIN is not allowed by Access-Control-Allow-Origin".
I think that this is a classical cross domain issue but how to solve it? I've added my domain to my facebook application (at https://developers.facebook.com/apps) and Facebook has a paragraph which is called "Delete scores for a user". So it must be possible to delete the scores (somehow)?
Because of Cross-Site-Scripting (XSS) a HTTP DELETE is not possible. But you can send a HTTP POST request with the query parameter ?method=delete, which then deletes the score.
Code Sample:
Facebook.prototype.deleteUsersHighScore = function()
{
var deleteHighScoreUrl = 'https://graph.facebook.com/'+this.user.id+'/scores?access_token='+this.application.id+'|'+this.application.secret+'&method=delete';
jQuery.ajax(
{
type: 'POST',
async: false,
url: deleteHighScoreUrl,
success: function(data, textStatus, jqXHR)
{
console.log('Score deleted.');
}
});
}
This is the Cross Domain security issue.
The fact that your error contains the message "Origin MY_DOMAIN" would tell me that somewhere in your code you have copied one of Facebook's examples but not changed the value for MY_DOMAIN to the correct domain you are using.
I would check all of your code for the value "MY_DOMAIN".
Please ignore this advice if you have changed the value to hide your actual domain in your question.