Customized error messages for $httpProvider.interceptors - javascript

We have implemented the below
function Inteceptors($httpProvider) {
'ng-inject';
$httpProvider.interceptors.push('ErrorInterceptor');
$httpProvider.interceptors.push('LoadingInterceptor');
}
function ErrorInteceptor($q, MyNotificationService) {
'ng-inject';
return {
responseError: function(response) {
var msg = JSON.stringify(response.data);
var status = response.status;
console.log('in ErrorInterceptor', response);
if (response.status === -1) {
status = null;
msg = 'An unspecified error occured while trying to make a request'
}
var notification = {
type: 'error',
status: status,
msg: msg
};
MyNotificationService.add(notification);
return $q.reject(response);
}
};
}
This allow errors like 404 and 500 to be intercepted and a message is being prompted to the users.
However, there are certain circumstances that I would like to make use of my own customized error message.
For example when I have a function that makes a call to an API:
this.list = function() {
return $http({
method: 'GET',
url: myendpoint
})
.then(function(response) {
return response.data;
},
function(err) {
return [];
});
}
The response looks like the below in case of 404:
- Object
-- config: Object
- data: Object
- headers: (name)
- status: 404
- statusText: "Not Found"
- __proto__: Object
so if the API returns 404, right now the interceptor is displaying response.data which is "Not found" and status is 404 in response.status
So the message now is
(404) {"detail": "Not found"}
And that is ugly and not helpful!
I would like to provide my own customized messages, how would I achieve that?

If I understand your question correctly then you want to return custom error from ErrorInteceptor() function. You are getting this same error because you are returning response i.e. return $q.reject(response); So try to return your custom message from your service.
Try this
return {
responseError: function(response) {
var status = response.status;
console.log('in ErrorInterceptor', response);
if (response.status === -1) {
status = null;
msg = 'An unspecified error occured while trying to make a request'
}
var notification = {
type: 'error',
status: status,
msg: msg
};
MyNotificationService.add(notification);
return $q.reject(response.statusText);// this is what you should return
}
};

Related

How to fix "Error: Can't set headers after they are sent" in Express

I have recently been developing a MERN application and I have recently came into the trouble that express is saying that I am setting headers after they are sent.
I am using mongo db and trying to update a user profile.
I have tried to comment out my res.send points to find the issue but I have failed to do so.
Here is my post method for updating the user profile:
app.post("/api/account/update", (req, res) => {
const { body } = req;
// Validating and Checking Email
if (body.email) {
var email = body.email;
email = email.toLowerCase();
email = email.trim();
body.email = email;
User.find(
{
email: body.email
},
(err, previousUsers) => {
if (previousUsers.length > 0) {
return res.send({
success: false,
message:
"Error: There is already another account with that email address"
});
} else {
}
}
);
}
// Validating Names Function
function checkName(name) {
var alphaExp = /^[a-zA-Z]+$/;
if (!name.match(alphaExp)) {
return res.send({
success: false,
message: "Error: Names cannot contain special characters or numbers"
});
}
}
checkName(body.firstName);
checkName(body.lastName);
// Making sure that all fields cannot be empty
if (!body.email && !body.firstName && !body.lastName) {
return res.send({
success: false,
message: "Error: You cannot submit nothing"
});
}
// Getting User ID from the current session
UserSession.findById(body.tokenID, function(err, userData) {
// Finding User ID using the current users session token
if (userData.isDeleted) {
return res.send({
success: false,
message:
"Error: Session token is no longer valid, please login to recieve a new one"
});
}
// Deleting the token ID from the body object as user table entry doesnt store tokens
delete body.tokenID;
// Finding the user profile and updating fields that are present
User.findByIdAndUpdate(userData.userId, body, function(err, userInfo) {
if (!err) {
return res.send({
success: true,
message: "Success: User was updated successfully"
});
}
});
});
});
This is the call that I am doing to the backend of the site:
onUpdateProfile: function(fieldsObj) {
return new Promise(function(resolve, reject) {
// Get Session Token
const obj = getFromStorage("the_main_app");
// Defining what fields are getting updated
fieldsObj.tokenID = obj.token;
// Post request to backend
fetch("/api/account/update", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(fieldsObj)
})
.then(res => {
console.log("Verify Token - Res");
return res.json();
})
.then(json => {
console.log("Verify Token JSON", json);
if (json.success) {
window.location.href = `/manage-account?success=${json.success}`;
} else {
window.location.href = `/manage-account?success=${json.success}`;
}
});
});
}
Here is my error message that I am getting:
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at ServerResponse.header (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\express\lib\response.js:158:21)
at C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\routes\api\account.js:270:22
at C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\mongoose\lib\model.js:4641:16
at process.nextTick (C:\Users\kieran.corkin\Desktop\Projects\Mern Template Final\mern-cra-and-server\server\node_modules\mongoose\lib\query.js:2624:28)
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
[nodemon] app crashed - waiting for file changes before starting...
Can anyone help me with this?
EDIT
I have changed my code, this seems to now work however I feel like its a little messy when put together. Any refactoring tips?
Code:
app.post("/api/account/update", (req, res) => {
// Preform checks on data that is passed through
const { body } = req;
var messages = {
ExistedUser:
"Error: There is already another account with that email address",
NameFormat: "Error: Names cannot contain special characters or numbers",
BlankInputs: "Error: You cannot submit nothing",
accountLoggedOut:
"Error: Session token is no longer valid, please login to recieve a new one",
successfullyUpdated: "Success: User was updated successfully"
};
var usersFound;
if (body.email) {
var email = body.email;
email = email.toLowerCase();
email = email.trim();
body.email = email;
User.find(
{
email: body.email
},
(err, UserCount) => {
usersFound = UserCount;
}
);
}
function capitalize(text) {
return text.replace(/\b\w/g, function(m) {
return m.toUpperCase();
});
}
if (body.firstName) {
body.firstName = capitalize(body.firstName);
}
if (body.lastName) {
body.lastName = capitalize(body.lastName);
}
//Making sure that all fields cannot be empty
if (!body.email && !body.firstName && !body.lastName) {
return res.send({
success: false,
message: messages.BlankInputs
});
}
// Getting User ID from the current session
UserSession.findById(body.tokenID, function(err, userData) {
// Finding User ID using the current users session token
if (userData.isDeleted) {
return res.end({
success: false,
message: messages.accountLoggedOut
});
}
if (userData) {
// Deleting the token ID from the body object as user table entry doesnt store tokens
delete body.tokenID;
// Finding the user profile and updating fields that are present
User.findByIdAndUpdate(userData.userId, body, function(err, userInfo) {
if (userInfo) {
if (!usersFound.length > 0) {
return res.send({
success: true,
message: messages.successfullyUpdated
});
} else {
return res.send({
success: false,
message: messages.ExistedUser
});
}
}
});
}
});
});
You're calling res.send() twice. res.send() ends the process. You ought to refactor such that you call res.write() and only call res.send() when you're done.
This StackOverflow link describes the difference in more detail. What is the difference between res.send and res.write in express?
I believe this is happening, as you're trying to send a response after the first / initial response has already been sent to the browser. For example:
checkName(body.firstName);
checkName(body.lastName);
Running this function twice is going to try and yield 2 different "response" messages.
The product of a single route, should ultimately be a single response.
Thanks for all your help on this issue.
Here is my final code that allowed it to work.
I have also tried to "refactor" it too. Let me know if you'd do something else.
app.post("/api/account/update", (req, res) => {
const { body } = req;
console.log(body, "Logged body");
// Defining objects to be used at the end of request
var updateUserInfo = {
userInfo: {},
sessionToken: body.tokenID
};
var hasErrors = {
errors: {}
};
// Checking that there is at least one value to update
if (!body.email && !body.firstName && !body.lastName) {
var blankError = {
success: false,
message: "Error: You cannot change your details to nothing"
};
hasErrors.errors = { ...hasErrors.errors, ...blankError };
} else {
console.log("Normal Body", body);
clean(body);
console.log("Cleaned Body", body);
updateUserInfo.userInfo = body;
delete updateUserInfo.userInfo.tokenID;
}
// Function to check if object is empty
function isEmpty(obj) {
if (Object.keys(obj).length === 0) {
return true;
} else {
return false;
}
}
// Function to remove objects from body if blank
function clean(obj) {
for (var propName in obj) {
if (obj[propName] === "" || obj[propName] === null) {
delete obj[propName];
}
}
}
// Checking and Formatting Names Given
function capitalize(text) {
return text.replace(/\b\w/g, function(m) {
return m.toUpperCase();
});
}
if (body.firstName) {
body.firstName = capitalize(body.firstName);
}
if (body.lastName) {
body.lastName = capitalize(body.lastName);
}
// Checking and formatting email
if (body.email) {
body.email = body.email.toLowerCase();
body.email = body.email.trim();
// Checking for email in database
User.find({ email: body.email }, (err, EmailsFound) => {
if (EmailsFound.length > 0) {
var EmailsFoundErr = {
success: false,
message: "There is already an account with that email address"
};
hasErrors.errors = { ...hasErrors.errors, ...EmailsFoundErr };
}
});
}
// Getting User Session Token
UserSession.findById(updateUserInfo.sessionToken, function(err, userData) {
// Finding User ID using the current users session token
if (userData.isDeleted) {
var userDeletedError = {
success: false,
message:
"Your account is currently logged out, you must login to change account details"
};
hasErrors.errors = { ...hasErrors.errors, ...userDeletedError };
} else {
// Finding the user profile and updating fields that are present
User.findByIdAndUpdate(
userData.userId,
updateUserInfo.userInfo,
function(err, userInfo) {
// userInfo varable contains user db entry
if (err) {
var updateUserError = {
success: false,
message: "Error: Server Error"
};
hasErrors.errors = {
...hasErrors.errors,
...updateUserError
};
}
if (isEmpty(hasErrors.errors)) {
res.send({
success: true,
message: "Success: You have updated your profile!"
});
} else {
res.send({
success: false,
message: hasErrors.errors
});
}
}
);
}
});
});

How to return the response status

I have connections to the database written in Angularjs
$http({
url: '/api/v1.0/relations/status/' + angular.element('#username').val(),
method: "GET"
})
.then(function (result) {
...
})
.catch(function (error) {
if(error=== 403) {
$scope.divRelationship = false;
}
});
Try to retrieve the status code from the error, however, you cannot
angular.js:12587 GET http://localhost:8080/api/v1.0/relations/status/jonki97 403 ()
Receives such an error in the console. How can we remedy this?
The error object is actually a response object, which contains the property 'status'.
So your code would need to look like:
$http({
url: '/api/v1.0/relations/status/' + angular.element('#username').val(),
method: "GET"
})
.then(function (result) {
...
})
.catch(function (error) {
if(error.status === 403) {
$scope.divRelationship = false;
}
});
Source: https://docs.angularjs.org/api/ng/service/%24http#general-usage

passing error message from service to controller in angularjs

Controller.js
var vm = this;
vm.admin = {};
vm.add = function () {
API.addAdmin(token, vm.admin)
.then(function (resp) {
vm.hideForm = true;
vm.showButton = true;
Notify.green(resp);
}, function (resp) {
Notify.red(resp);
});
};
API.js
function addAdmin(token, dataObj) {
return Constant.getApiUrl()
.then(function (url) {
$http({
method: 'POST',
url: url + '/client/admin',
headers: {
'Token': token
},
data: dataObj
}).then(handleResp);
function handleResp(resp) {
var responseStatus = (resp.status >= 200 && resp.status < 300) ? 'good' : 'bad';
if (responseStatus === 'good') {
console.log("Success" + resp);
return resp;
} else {
console.log("Failed" + resp);
return resp;
}
}
})
}
If I get a success response in API then i need to connect it to success function in my controller and if i get error message in my API, then i need it to connect it to error function in my controller.How should I evaluate the response status from my API(is either success or error).
I don't want to pass successfn, errorfn from my controller to API(only if there's no alternative).
I need to get the response data from API to controller to show it in Notify message.
Thank You!
In service (assign response values in "originalData"):
angular.module('appname').service('myserviceName', function(yourExistingService){
this.myFunction= function(originalData) {
//for next line to work return promise from your addAdmin method.
var promise = yourExistingService.getResponseFromURL(originalData);
return promise;
}
});
And in your controller :
var promise = myserviceName.myFunction($scope.originalData);
promise.$promise.then(function() {
console.log($scope.originalData);
});
And then you can check you "originalData" and write code according to your need.For more detail you can have a look on this http://andyshora.com/promises-angularjs-explained-as-cartoon.html.

JS/Ionic:TypeError: Cannot read property 'code' of undefined

I am just a beginner in JS. I am trying to log in from the db using php RESTful web service. If the login credentials match the user is shown the home page.
The error I am getting is: TypeError: Cannot read property 'code' of undefined at services.js:65
What should I do to solve this error? And why did this error happen?
My services.js is:
.service('LoginService', function ($q, $http) {
return {
loginUser: function (loginData) {
var deferred = $q.defer(),
promise = deferred.promise;
$http({
url: 'http://localhost/login.php',
method: "POST",
data: loginData,
headers: {'Content-Type': 'application/json'}
})
.then(function (response) {
if (response.data.error.code === "000") {
console.log("User login successful: " + JSON.stringify(response.data));
deferred.resolve(response.data);
} else {
console.log("User login failed: " + JSON.stringify(response.data.error));
deferred.reject(response.data);
}
}, function (error) {
console.log("Server error on Login " + JSON.stringify(error));
deferred.reject(error);
});
promise.success = function (fn) {
promise.then(fn);
return promise;
};
promise.error = function (fn) {
promise.then(null, fn);
return promise;
};
return promise;
}
};
});
It seems like response.data.error is returning as undefined (or returning nothing, which leads undefined), that is the reason, if you try to read response.data.error.code, you get that error.
You can try putting a breakpoint on if (response.data.error.code === "000") { and see what is the output in response and check accordingly on errors.

AngularJs: Exclude some requests from Interceptor

Below is my interceptor which handles global errors. But I want to bypass some http requests. Any suggestions ?
var interceptor = ['$rootScope', '$q',function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
window.location = "./index.html#/404";
return;
}
if (status == 0) {
window.location = "./index.html#/nointernet";
}
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
I was able to implement this functionality simply by adding a property to the config object of the $http request. ie. ignore401. Then, in my interceptor, in the response error handler, check for the property on the config object, and if it is present, do not forward to login or whatever else you do on a 401 response.
First, the interceptor:
$provide.factory('authorization', function() {
return {
...
responseError: (rejection) => {
if (rejection.status === 401 && !rejection.config.ignore401) {
// redirect to login
}
return $q.reject(rejection);
}
};
});
Then, for any request that I want to bypass the 401 error handler:
$http({
method: 'GET',
url: '/example/request/to/ignore/401error',
ignore401: true
});
Hope that helps.
I'm solving this as follows:
$httpProvider.interceptors.push(function($rootScope) {
return {
request: function(config) {
var hideUrl = "benefit/getall"; // don't show the loading indicator for this request
var hide = (config.url.indexOf(hideUrl)); // is this isn't -1, it means we should hide the request from the loading indicator
if(hide == -1)
{
$rootScope.$broadcast('loading:show')
}
return config
},
response: function(response) {
$rootScope.$broadcast('loading:hide')
return response
}
}
});
Doesn't the response object contain an "options" property where you can inspect what URL was used in the request?

Categories

Resources