Purposefully Invoke an Error in $http Post Request - javascript

I'm using Angular to send a post request with the $http service. I am doing all of the data validation of the form in Angular prior to sending the post request. However, there is one validation I am doing in PHP of whether the user already exists in the database. How do I purposefully invoke an error (in the php file) so that the Angular error callback is triggered instead of the success callback? Should I purposefully throw an exception?
IF the intent is to throw an exception, does the exception message get passed into the data parameter for the Angular error callback function?

Based on the comments to my question, I just did the following to my code:
if (duplicateUsers($username) > 0) {
return http_response_code(400); // successfully generated an error in
// the $http AngularJS servicces
} else {
// other code
}

You can chain your promises. The first promise will check the success content and that's also where you can throw an exception. This will cause the subsequent promises to return failure.
Here's a jsbin example.
angular
.module('app', [])
.run(function($http) {
var from$http = $http
.get('www.google.com') //makes a request to www.google.com
.then(function(response) {
console.log('data was successfully retrieved from google');
throw "from success handler"; //if has error, then throw "duplicated user"
});
from$http.then(function() { // this then block is handling the previous exception
console.log('this success block is never called');
}, function() {
console.log('inside error block even tho success was returned from www.google.com');
});
});

Related

Handling server side errors in javascript fetch

I'm making a script which has server side validation. When validation fails I have the script throw an exception server side. I want to access the message in the exception when the response reaches the client side, but I'm having trouble with that. If I console.log the data I can see the exception, but I want to access it in the catch so I can push the error message to an HTML element on the page.
fetch(/* Posting to some PHP script */).then(function(data) {
// Logic here when success
console.log(data) // Shows error msg when there is an error
}).catch(function() {
// Error Handling, I want to push the error msg to HTML element here
});
What am I doing wrong here?
Thanks
Throw the error. It will be caught in the .catch() and you can manipulate it there
fetch(/* Posting to some PHP script */)
.then(function(data) {
// Logic here when success
console.log(data) // Shows error msg when there is an error
if(data.error){ // Or whatever condition you need to detect there's an error
throw data.error; // This will be caught in the catch() below
}
})
.catch(function(err) {
// You have your error here with the message from the server, display it in the HTML
});

How do I get the post json when there is a 404 error?

I have a service call that when it returns a 404 error, I want to display the message that comes from the server when the status is 404. So, in event of an error or success, I get a post json that gives me a status code and message that indicates if it was successful or not.
Currrently, I have this service call:
this._transactionService.findExistingTransaction(user, searchNumber)
.subscribe(data => {
this.transactionResponse = data;
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal'], {queryParams: {bill: searchNumber}});
this.onDismiss();
}, (err) => { this.displayErrors = true;});
on error, it will set the bool displayErrors = true and then I can show the error message in my UI.
In html code:
<input #inputtedNumber class="transactionInput" placeholder="{{numberPlaceholder | translate }}"/>
<div class="error-msg1" *ngIf="displayErrors" style="margin-left:90px;" name="errorMsg">
{{transactionResponse._errorDetails._message}} </div>
This is the json that gets posted back when I directly try to access api endpoint:
{
"_transactionNumber":null,
"_order":null,
"_errorDetails":{
"_status":"404",
"_message":"Number is not available"
}
}
I bind to the transactionResponse object that I get back from my service call. Unfortunately, although I believe this should work, I get the issue that _errorDetails is undefined and so nothing shows up.
I wonder if this is the right setup for something like this? If now, how can I fix it?
Thanks!
EDIT: Duplicate SO post with no answer: How to read Custom error message from backend in Angular 4/2
The response body from the server should be in the error property of the error response that comes back in the error callback.
Regarding HttpErrorResponse, the documentation states:
A response that represents an error or failure, either from a non-successful HTTP status, an error while executing the request, or some other failure which occurred during the parsing of the response.
Any error returned on the Observable response stream will be wrapped in an HttpErrorResponse to provide additional context about the state of the HTTP layer when the error occurred. The error property will contain either a wrapped Error object or the error response returned from the server.
If you want to use the same transactionResponse to display the errors, then assign the error property of the err that comes back to this.transactionResponse.
Service Call
this._transactionService.findExistingTransaction(user, searchNumber).subscribe(
(data) => {
this.transactionResponse = data;
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal'], {queryParams: {bill: searchNumber}});
this.onDismiss();
},
(err: HttpErrorResponse) => {
this.displayErrors = true;
// assign the error property of the err that comes back to the transactionResponse
this.transactionResponse = err.error;
});
HTML
Then this will work.
<input #inputtedNumber class="transactionInput" placeholder="{{ numberPlaceholder | translate }}"/>
<div class="error-msg1" *ngIf="displayErrors" style="margin-left:90px;" name="errorMsg">
{{transactionResponse._errorDetails._message}}
</div>
There was some work done to this part of Angular in September 2017. parse error response body for responseType "json" So you may need to update Angular depending on your version.
This solution was tested on the following:
Node v8.2.1
NPM v5.3.0
Angular CLI: 1.7.2
Angular: 5.0.0
Edit: StackBlitz example
HttpErrorResponse StackBlitz example
This example makes some assumptions about what the service looks like and what endpoint it is calling. The service makes a POST call to www.google.com. This fails and returns an HttpErrorResponse.
{
"isTrusted": true
}
The error property of the HttpErrorResponse is assigned to this._transactionResponse. This can then be accessed in the template and displayed in the browser.
Your problem is that in the event of an error, your
data => {
this.transactionResponse = data;
code does not get called - you got an error response, not a normal response afterall.
Try to get the information from the
}, (err) => { this.transactionResponse = err
part.
I think you can use a typed response:
On your error notification type you could have something like:
err => {
this.localErrorResponse = err as ErrorResponse;
this._order= this.localErrorResponse._order;
}
inside your class, also, you could have:
import { ErrorResponse } from './error-response';
localErrorResponse: ErrorResponse;
_order: string;
and then, you could have your ErrorResponse class like:
import { ErrorDetail } from './error-detail';
export class ErrorResponse{
_transactionNumber: number;
_order: string;
_errorDetails: ErrorDetail;
}
and class ErrorDetail
export class ErrorDetail {
_status: number;
_message: string
}
then you can map some other variables, as _order (this._order), or get them from your localErrorResponse (this.localErrorResponse) variable
I have a service call that when it returns a 404 error, I want to
display the message that comes from the server when the status is 404
...
I bind to the transactionResponse object that I get back from my
service call. Unfortunately, although I believe this should work, I
get the issue that _errorDetails is undefined and so nothing shows up.
Try this:
StackBlitz EXAMPLE

AngularJS - trouble with $http's success() and error() not being called properly

I am working on this code and I have a really weird issue. I am using AngularJS $http request, and trying to run a success or error afterwards. I am currently getting a 404 from the server (the client says) but the server isn't barfing up a 404 (in fact it says it sent a 200 response).
This $http post doesn't hit either the 'success' or 'error' handlers and I don't know what to do for debugging it.
All other functions using RequestService work perfectly.
var refreshBusinesses = function() {
console.log("refreshBusinesses");
RequestService.post('user/managed_businesses', {}).then(function(businesses){
console.log('got busineses ', businesses)
$scope.businesses = businesses
}, function(error){
console.log("ERROR!");
$scope.error = error.message;
console.log('business refresh error ', error)
})
}
Most probably your service RequestService does not handle promise properly. I believe it is a wrapper around angular's $http. If so, probably it does not reject promise on error, but simply returns some value.
So to solve it check your RequestService that it handles error situation properly:
$http.post('url', data).then(
function(res) {
return res;
},
function(error) {
var deferred = $q.defer();
return deferred.reject(error);
});
$q docs can be found here https://docs.angularjs.org/api/ng/service/$q

How should AngularJS handle 403 error in $http.post due to outdated XSRF token?

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.

try catch on javaScript does not work

I have a code for reading a json file in JS. I had the code wrapped in "try catch" blocks.
The way I get it - if the file is not found for some reason, it should enter the "catch" block.
Yet, if "lang" is undefined - it throws a 404:
" GET --<-server->--/settings/Lang_undefined.json 404 (Not Found) "
without ever entering the "catch" block.
can anyone help me understand why it is so?
Thank you!!
function loadDic() {
try {
$.getJSON(SERVER_URL + "settings/Lang_" + lang + ".json", function(data) {
dic = data["dictionary"];
setLanguage();
});
} catch (err) {
return false;
}
}
Since you appear to be making an ajax request (not a jsonp request,) you can catch the error using the error callback.
function loadDic() {
$.getJSON(SERVER_URL + "settings/Lang_" + lang + ".json", function(data) {
dic = data["dictionary"];
setLanguage();
}).fail(setLanguageFail);
}
Ajax is asynchronous.
The getJSON function sends an HTTP request.
It sends it successfully
The try is successful
The Ajax then gets a response saying that the server had a 404 error
You haven't specified an error event handler, so nothing happens
The 404 error does not cause an exception to be thrown (it is an HTTP error, not a JavaScript error) and there is nothing waiting to catch an exception at that time anyway.
If you want to do something in the event of an error, then specify a function to handle it (you can't do this with getJSON(), you should use ajax() instead). Note that you cannot return (based on this) from the function that included the getJSON call.

Categories