Abort accessing success callback in backbone model - javascript

I have a backbone model which consist of following parse method.
parse: function(response, options){
// Handling Error for http response 200
if( response.Reasons && response.Reasons[0] ){
var errorResponse = {
responseJSON: {
reasons: [
{
code: response.Reasons[0].ReasonCode,
reason: ''
}
]
}
}
options.error( errorResponse );
return errorResponse;
}
}
My issue is, my web service won't give http response 500 for errors. I need to check it from the response & decide that. But the problem is, above code runs the error callback but after that it goes to the success callback as well. I need to stop that.
I receive a json response from the server, similar as following
{Data: {some data}}
for error
{Data: {}, Reasons: [{error code}]}
Please let me know how can i do that. Thanks in advance

Backbone by default expects the response to be JSON, so if JSON is returned from the server the success callback is triggered. With out overriding the fetch method of backbone, you can't stop from triggering success.

Related

Code is not capturing 404 errors in angularjs $http request

I'm trying to add some error handling to an angularjs app I wrote about a year ago. For some REST API calls, the back end returns a 404 that I want to silently fix up by simply returning an empty list/array. But I'm not able to capture the 404 error and I don't understand why it's not working.
I'm following the docs here and I expect the response of the errorCallback to be an object with a status property. Instead, it has a SyntaxError constructor with properties named fileName, lineNumber, columnNumber, and message. The message is a JSON.parse error.
Now I can -- kind of -- understand why there is a JSON parse error: there is no data! But the response status is being lost and my code cannot distinguish between a 404 and other errors that I do not want to silently fix up.
What am I missing here? And where is it documented? The angular docs seem incorrect -- unless I'm somehow reading them wrong.
$http({
method: 'GET',
url: myRestApiUri
})
.then(
function successCallback( response ) {
handlers.success(response);
},
function errorCallback( response ) {
if( response.status === 404 ) {
handlers.success( [] );
}
else {
handlers.error(response);
}
}
)
EDIT: After receiving some feedback, I realized that I'm using an older version of angular. So I'm revising my question slightly: Does the latest (or any newer) version of angular implement the http error trapping correctly? I don't want to install a new angular and rework my code for API changes, only to find out then that the newer angular has the same problem.

Backbone Response on Sync

First of all my knowledge of Backbone is very limited, and currently, I'm trying to add an implementation into some code I didn't create.
My problem is as follows:
I have a collection which is being rendered on click of a button. Now, we are setting some permissions on the website, so that sometimes the response I will get is a 401.
I'm currently able to get the response, the problem is that I don't know how to attach it to the sync event so that if I get a 401 when I call the API, it shouldn't render anything.
I would think looking at the code would help clarify my problem:
this.addressBook = new (Backbone.Collection.extend({
url: url,
model: Backbone.Model.extend({
idAttribute: 'ID'
}),
parse: function(data) {
return data;
}
}))();
this.addressBook.on('sync', this.renderAddresses, this);
this.addressBook.fetch();
So I found a few ways to get the status code from fetch, but in this particular case, I need to get the status code before the sync event calls this.renderAddress, and given the status of the response, go ahead and render my view or simply display a message stating that access is denied.
Sorry if I'm not clear enough.
Thanks in advance.
Here is the salient part of the backbone documentation:
Whenever a model or collection begins a sync with the server, a "request" event is emitted. If the request completes successfully you'll get a "sync" event, and an "error" event if not.
This means that the sync event shouldn't fire if you get a 401, rather the error event should be triggered.
You can test this in your code by listening to all the different events (all available parameters included):
this.listenTo(yourCollection, 'request', function(collection, resp, options) {
console.log('Request: ', resp);
});
this.listenTo(yourCollection, 'error', function(collection, resp, options) {
console.log('Error: ', resp);
});
this.listenTo(yourCollection, 'sync', function(collection, resp, options) {
console.log('Sync: ', resp);
});
So you should be able to just listen to the error event to display your custom error message:
this.listenTo(yourCollection, 'error', function(collection, resp) {
if (resp.status === 401) {
console.warn('401: Unauthorized');
} else {
// do something else
}
});
First of all, you will need to define some callback options for you addressBook fetch. For what you are trying to accomplish, you need to provide the error callback.
this.addressBook.fetch({
error: function(xhr) {
if(xhr.status == 401) { // Put logic you want in the case of 401 here...
}
});
Disclaimer: I have not tested this code, as I do not have a convenient way I can think of to reproduce this problem.
I believe it works because Backbone passes a jQuery xhr Object to its error callback. http://backbonejs.org/#Model-fetch

Angular issue with Q library

I am following the tutorial related to Angular.js that is located on pluralsight.com. So far I did not have serious issues. Tutorials are very good and easy to follow. However, there is something that I cant resolve on my own. I would like my deffereds to react differently either on success or on fail. In this case they are always firing like they succeeded.
Service:
var resource = $resource('/data/event/:id', {id: '#id'});
return {
getEvent: function (id) {
var deferred = $q.defer();
resource.get({id: id},
function (event) {
console.log("This is (EVENT): " + event);
deferred.resolve(event);
},
function (response) {
console.log("This is (RESPONSE): " + event);
deferred.reject(response);
});
return deferred.promise;
Controller:
$scope.event = eventData.getEvent(2)
.then (
function(event) {
$scope.event = event;
console.log(event);
},
function(response) {
console.log(response);
}
);
In other words, if I send the wrong id (to load JSON file that does not exists) I want it to let me know that.
The $resource service in angular wraps a call to the $http service applying some REST conventions to the HTTP request. These are all documented here in the $resource docs.
When handling the promise from $resource, if the HTTP status code is 200, then the success callback will be executed. Otherwise (if the HTTP status code is in the 400 or 500 range), the error callback will be executed. This is a typical REST convention.
So, since your service is always returning 200 Status codes, $resource assumes that this is a successful server call and executes the success callback.
In order to handle this, you would need to use $http directly or change your service so that it returns the correct HTTP status code.
Hope this helps.

Backbone Create

For some reason this.collection.create() is returning an error, but interestingly, the error message seems like the model I just added to my collection.
this.collection.create({
name: $('#big-input').val(),
firstRemark: $('#small-input').val(),
postedBy: window.user.displayName,
twitterHandle: window.user.twittername,
pictureUrl: window.user.profilePic
},{wait: true,
success: function(response){
console.log("success");
console.log(response)
},
error:function(err){
console.log(err)
}
});
this is what I get after console.log(err):
exports.newPost = function(req, res){
console.log("GOT NEW TWEET REQUEST");
var newPost = new Post(req.body)
newPost.dateCreated = new Date();
newPost.save();
res.send(200);
};
Thanks to answers below I was able to print my 'real' error. As seen below xhr.responseText is 'OK' and 'status' is 200. Why is this response triggering a success but an error?
I also have a parse method in my collection
parse: function(response){
this.page = response.page;
this.perPage = response.perPage;
this.total = response.total;
this.noofpages =response.noofpages;
return response.posts;
},
This is expected. Have a look at the Model.Save documentation, which says the error callback will be called with (model, xhr, options) as it's parameters.
If you want the actual contents of the response you can get it from the second parameters responseText property: xhr.responseText. There's more details on jqXHR elements in the jquery documentation: http://api.jquery.com/jQuery.ajax/#jqXHR
The parameters of your success callback are also not quite right - it takes (model, response, options)
EDIT:
Not entirely sure if it's the cause of your problems, but your server should be returning a 200 status code and the models JSON on success. Have a look at the Backbone.Sync documentation.
From looking at the code this does look important when passing wait: true as an option, as the attributes set are extended with the returned attributes from the server. Have a look at the options.success function used by backbone here to see what I mean. It certainly looks like something might go wrong if you return "OK" from the server, though I'm not sure if it'd be exactly the problem you're experiencing.
EDIT2: Slight correction to what I wrote above: The return value gets passed through Model.parse. Since you've defined a custom parse function, the server should return something that is going to work with it, rather than just plain JSON for the model.
The error function will receive 3 arguments as defined in the wrapError function in backbone.js
model.trigger('error', model, resp, options);
Therefore what your output is, is correct. You will want to add a variable to your error function to capture the response (2nd argument) and log that instead to help you debug.

After creating new instance in collection, don't do a GET request on the endpoint (backbone)

After I add a model instance to a collection, I do a POST request to add it. Then a GET request is done to get the model from the server. Is there a way to not to the GET request, only the POST request? Also, is it possible to get the success and error callback functions to respond to the success and failure of the POST request?
I want to do this because the collection has a URL that parses the JSON data that gets back, so the GET request doesn't work, but the POST request does work. I don't want to do a GET request on a endpoint that doesn't work.
The GET request is unnecessary. On the server in your POST handler you should return a JSON result back to the client representing the model. This is especially useful when there are generated fields such as an id. Then on the client in the success callback you can grab the model returned from the POST.
In the following example a new model is added to the collection if successful. I've also included the error callback which will fire if either client side validation fails or the POST fails:
var isNew = this.model.isNew();
this.model.save({}, {
success: function(model, response) {
if (isNew && this.collection) {
this.collection.add(model);
}
},
error: function(model, response) {
var errorMsg;
// Response may be string (if failed client side validation or an AJAX response (if failed server side)
if (_.isString(response))
errorMsg = response;
else
errorMsg = response.responseText;
}
});
The process you follow is indeed unnecessary. You should be using create on the collection to directly add the model, and invoke sync (the POST in this case) in the same time.
For example:
collection.create({foo: 'bar'}); or collection.create(unsaved_model);
The result of invoking create will return either the (saved) model or false if this was not successful. In addition it is possible to wait for the model to be saved before adding to the collection by doing
collection.create({foo: 'bar'}, {wait: true});
The documentation is your friend.

Categories

Resources