I have an API built in Laravel which returns JSON in a format such as this:
{
"data":{
"errors":{
"username":"The username has already been taken.",
"email":"The email has already been taken."
}
},
"success":true,
"status":400
}
In this case, I'm trying to create a user with a username and email address which already exists. This is the Angular $resource code I'm using inside my controller for that:
var user = new User({
username: $scope.user.username,
email: $scope.user.email,
password: $scope.user.password
});
var response = user.$save(function(data) {
console.log(data);
}, function(data) {
if (data.status === 400) {
angular.forEach(data.data.data.errors, function(error) {
console.log(error);
});
}
});
So what this is doing is sending a POST request to /users on the API and if it comes back with a non-200 status code, it's checking if it's a 400 code which means a validation error and console.log'ing those error messages. Sure enough, I get the error messages output to the console.
What I'm wondering is, if there's a better way to access the error messages than data.data.data.errors. Seen as the API wraps the response data in a data field, and Angular returns the $resource of the request rather than the actual server response, it leads to a rather unsightly amount of properties being used to get the error messages.
Is there a better way to do this?
I think the short answer is 'no'. However, if it were me I would probably shuffle some of the variables to make things a bit nicer.
user.$save(function(data) {
console.log(data);
}, function(response) {
if (response.status === 400) {
var data = response.data.data
angular.forEach(data.errors, function(error) {
console.log(error);
});
}
});
If I had control over the api i would not be wrapping the errors in the redundant tertiary data object. This gives an entirely acceptable bit of code imo.
{
"errors":{
"username":"The username has already been taken.",
"email":"The email has already been taken."
},
"success":true,
"status":400
}
user.$save(function(data) {
console.log(data);
}, function(response) {
var data = response.data
if (data.status === 400) {
angular.forEach(data.errors, function(error) {
console.log(error);
});
}
});
Related
I have a project, a simple login form hooked to nodejs which is connected to MongoDB as the database.
relevant to this question
I use pure javascript, no jquery
my main aim in this project is to display error in the form without reloading the page (through a model) but if the information is correct or a user is found, the user is redirected to the profile page => now this is where the problem is.
this is my client request
// *the myModel is a bootstrap function that is used to notify users for error*
const myModel = new bootstrap.Modal(document.querySelector('.modal'), {
focus : true
})
document.querySelector("form").addEventListener("submit", function(e){
e.preventDefault()
data = {
username : document.forms[0].username.value,
password : document.forms[0].password.value
}
fetch("/login", {
method: "POST",
headers : { "content-Type": "application/json" },
body : JSON.stringify(data)
}).then(async(response)=>{
data = await response.json()
if(data.err){
console.log(data.redirect)
document.querySelector(".modal-body").innerText = data.err
myModel.show()
}else{
window.location.replace(data.redirect)
}
})
.catch(err=>{
document.querySelector(".modal-body").innerText = err.message
myModel.show()
})
})
this is my server-side code/response
.post(function(req,res){
console.log(req.body)
if(req.body.username.trim() && req.body.password.trim()){
const data = req.body
userCollection.findOne({username:data.username},function(error,back){
if(error){
console.log(error.message)
}else{
if(back){
console.log(back)
if(back.password === data.password){
res.redirect("/profile")
}else{
res.json({err : "password or username wrong, try again"})
res.end()
}
}
else{
console.log("no user found, check your inputs again")
res.json({err : "no user found, check your inputs again"})
res.end()
}
}
})
}else{
res.json({err:"username or password empty"})
}
everything works fine except when a user is found and the passwords match and I'm trying to redirect the user, the client-side request .catch keeps sending me an error "unexpected token < in json at position 4"
but if I change the res.redirect to maybe res.json("your account has been found") then it works fine without any error.
i've been stuck here for a day+ if you know how i will combat this, help for the sake of my mental health " haha
~~
I am trying to return a response or a error on the client side from a meteor method that exists on the server side. The method itself is for submitting a post request to a API when a form is submitted, I would like to on the client side be able to return the API's response so I can send info to the user like, this email address already exists. Here is what I have so far.
I receive a 201 response on the client side in the console.log(result); when I call the method when it is successful but I would like to also console log the error when the POST submissions gives a 400 or any error.
server/main.js
Meteor.methods({
addSub(email) {
var emailAddress = {
"lists": [
{
"id": "1395617465"
}
],
"email_addresses": [
{
"email_address": email
}
]
}
HTTP.post('https://api.constantcontact.com/v2/contacts?api_key=<api-key>', {
headers: {
'Authorization': 'Bearer <token>',
'Content-Type': 'application/json'
},
data: emailAddress
}, function (error, response) {
if ( error ) {
console.log( error );
throwError = error;
} else {
console.log(response);
returnResponse = response;
}
});
return returnResponse;
}
});
client/main.js
Template.info.events({
'submit #cm-subscribe'(event){
event.preventDefault();
var form = event.target;
var email = form.email.value;
Meteor.call('addSub', email, (error, result) => {
console.log(result);
});
}
});
client/main.html
<template name="info">
<h2>Signup For Our Newsletter</h2>
<form id="cm-subscribe">
<input field name="email" type="email" value="email">
<input type="submit">
</form>
</template>
1) Your code currently contains a race condition. The value of returnResponse may or may not be set depending on the order of execution between the callback and the return statement. Instead, you should use Promises, which allow you to wrap asynchronous code in a synchronous way. There is a good post on the Meteor blog about how to do this. Note that this does not affect the client behavior- it would still use callbacks like you have.
2) The reason that you do not see errors on the client is that Meteor intentionally converts all normal Javascript errors (like those returned by HTTP.post) into internal server errors to protect data and source code. What you should do is handle all of the errors on the server side, and return new Meteor.Error objects for expected invalid responses. These can then be handled by the client. A thorough explanation of this can be found in the Meteor Guide.
Something like this:
Meteor.methods({
addSub(email) {
return new Promise((resolve, reject) => {
HTTP.post('<URL>', ..., function(error, response){
if(!error){
resolve(response);
} else if (error == <Some Error Comparison>){
reject(new Meteor.Error("Email.subscription.invalidEmail", "You put in an invalid email"));
}
});
});
}
}
Is there a way to send an object to an API using axios?
This the code I use:
axios.get('/api/phones/create/', {
parameters: {
phone: this.phone
}
})
.then(response => {
console.log(response.data)
})
.catch(function (error) {
console.log(error)
})
on the php side, I have the following:
public function create($phone)
{
return $phone;
}
I get the following error:
GET http://crm2.dev/api/phones/create 500 (Internal Server Error)
dispatchXhrRequest # app.6007af59798a7b58ff81.js:256
xhrAdapter # app.6007af59798a7b58ff81.js:93
dispatchRequest # app.6007af59798a7b58ff81.js:662
app.6007af59798a7b58ff81.js:2266 Error: Request failed with status code 500
at createError (app.6007af59798a7b58ff81.js:600)
at settle (app.6007af59798a7b58ff81.js:742)
at XMLHttpRequest.handleLoad (app.6007af59798a7b58ff81.js:158)
If I try, axios.get('/api/phones/create/hello') I get hello in the console log.
Is there a way to do this?
It depends on what you mean by "send an object".
Since you're using a GET request and passing the object in the parameters, you can serialize it into query params as part of the GET request. This wouldn't really send the object but rather use it to build the query section of the URL for the GET request.
For example, here's how you can make a request to /api/phones/create?phone=123:
axios.get('/api/phones/create/', {
params: {
phone: '123'
}
})
If you want to actually send the object as a serialized JSON to your API, you can use a POST or a PUT request, depending on the semantics of your API.
For example, to send { "phone": "123" } to your api, you could do:
axios.post('/api/phones/create/', {
phone: '123'
});
Note: axios expects the key params for parameters.
First of all try with params instead of parameters.
Axios rely on promises you might need to add promise polyfill to your code if you want to support old browsers.
Here is sample request, Read official docs for more information.
axios.get('/url', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
I am trying to figure out how to handle an error when deleting or updating a document in MongoDB in Angular JS?
I have the following route in Node/Express:
function handleError(res, reason, message, code) {
console.log("ERROR: " + reason);
//log the reason for the error
res.status(code || 500).json({
"error": message
});
}
app.delete("/polls/:id", auth, function(req, res) {
db.collection(POLLS_COLLECTION).deleteOne({
_id: new ObjectID(req.params.id), userID: req.user.id
//userID must match the req.user.id from Passport to make sure the poll belongs to the user
}, function(err, doc) {
if (err) {
handleError(res, err.message, "Failed to delete poll");
} else {
res.status(204).end();
}
});
});
The following in an Angular JS controller:
$scope.deleteThisPoll = function(){
Polls.deletePoll($routeParams.pollId)
.then(function(response){
alert("Poll deleted!");
var url = "/mypolls/" + $scope.userID;
$location.path(url);
}, function(response){
alert("Error deleting poll");
console.log(response);
})
};
deleteThisPoll in the controller calls a deletePoll service that sends a a request to the route:
this.deletePoll = function(pollId){
var url = "/polls/" + pollId;
return $http.delete(url);
};
What I want is to alert "Error deleting poll" from the Angular controller when the database delete is not executed (because for example user is not authenticated or the poll doesnt belong to the user) and "Poll Deleted" when the delete was successfull.
However: the error callback is never used and the app always alerts "Poll deleted!" no matter if the document was deleted or not deleted.
Doesn't my route send an error response when the delete was not executed and will it not hit my Angular error callback?
You can do like code below
Put this HTML code where you want to show error message :
<div style="color:red;">
{{error}}
</div>
In your angular js controller :
$scope.deleteThisPoll = function(){
Polls.deletePoll($routeParams.pollId)
.then(function(response){
alert("Poll deleted!");
var url = "/mypolls/" + $scope.userID;
$location.path(url);
}, function(response){
$scope.error="Any error message you like to show";
console.log(response);
})
};
If your API return an error. you can catch it like this :
Polls.deletePoll($routeParams.pollId).then(function(response) {
//SUCCESS CODE
}).catch(function(error) {
//NOTIFY ERROR
//NotifyService.display(error);
console.log(error);
});
thanks guys. I found out that MongoDB for some reason always returns a result object even when there was no delete/update. I solved this by checking for the result.deletedCount propety that is set to 1 or 0. Like so:
if(err){
res.status(500).end();
}
if(result.deletedCount === 0){
res.status(404).end();
//error handling in Angular error callback
} else {
res.status(204).end();
//error handling in Angular success callback
}
});
});
this makes sure that not always a 204 is send whether or not the delete was successfull.
ive never used cloud code/javascript and I am trying to write some parse cloud code to find a user using a objectId passed in to the cloud function, and then update that users relation that holds friends and finally save that user.
below is the function im using:
Parse.Cloud.define("addFriendToFriendsRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var fromUserObjectId = request.params.fromUserObjectId;
var acceptingUser = request.params.user;
var query = new Parse.Query(Parse.User);
// find the user the request was from using the objectId
query.get(fromUserObjectId, {
success: function(user) {
var fromUser = user
var relation = fromUser.relation("friends");
relation.add(acceptingUser);
fromUser.save({
success: function() {
response.success("Successfully saved the users relation")
},
error: function() {
response.error("Save failed");
}
});
},
error: function() {
response.error("Save failed");
}
});
});
I managed to piece this together using the Parse docs. but Im really not following it to well. Never used javascript and am finding the syntax confusing.
then im calling the function with
//fromUser is a PFUser object defined further up
[PFCloud callFunctionInBackground:#"addFriendToFriendsRelation" withParameters:#{#"fromUserObjectId" : fromUser.objectId} block:^(id object, NSError *error) {
}
however whenever this function is called I get a success/error was not called error. Though im calling response.success and response.error in the function so I dont know why that is? Can anyone lend a hand?
edit: after doing some more searching it looks like response.success and response.error should only be called once each, so I modified my function to look like this:
arse.Cloud.define("addFriendToFriendsRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var fromUserId = request.params.fromUserObjectId;
console.log("fromUserId:");
console.log(fromUserId);
var acceptingUser = request.params.user;
console.log("acceptingUser:")
console.log(acceptingUser);
var query = new Parse.Query(Parse.User);
query.get(fromUserId, {
success: function(user) {
console.log("found user:");
console.log(user);
var fromUser = user;
var relation = fromUser.relation("friends");
relation.add(acceptingUser);
console.log("added accepting user to relation");
fromUser.save({
success: function() {
response.success("successfully saved user")
},
error: function() {
response.error("error saving user");
}
});
console.log("found a user");
},
error: function() {
console.log("error finding user");
}
});
});
An old question, but since it's been up-voted, maybe answering can help someone else :).
First off, there is an error in how you are saving fromUser.
fromUser.save({ success: ...
If you look at the api you can see that it should be of the form:
fromUser.save(null, { success: ...
But the larger problem that kept you from finding your bug is that errors are getting eaten 'cause you are using the old style method of dealing with async code instead of using promises.
Below, I have re-written to use promises. Note:
I always return promise generating calls (there are other options for catching errors in async code, but start with this.)
Put a .catch at the end. The .catch is effectively the same things as .then(null, response.error) but either way, it is imperative that there is final backstop to catch errors. In your code above, the error was in a success block, that was running async, so when there was an error, it failed with no one to hear it :).
Parse.Cloud.define("addFriendToFriendsRelation", (request, response) => {
const fromUserId = request.params.fromUserObjectId;
console.log("fromUserId:", fromUserId);
const acceptingUser = request.user;
console.log("acceptingUser:", acceptingUser)
new Parse.Query(Parse.User);
.get(fromUserId, { useMasterKey: true })
.then((fromUser) => {
console.log("found fromUser:", fromUser);
const relation = fromUser.relation("friends");
relation.add(acceptingUser);
console.log("added accepting user to relation");
return fromUser.save(null, { useMasterKey: true })
})
.then(response.success)
.catch(response.error);
});