Backbone sync error even after response code 200 - javascript

Hello Backbone ninjas,
This is my first time using Backbone - so please excuse my "noob"ness.
In my functionality (part of a larger app),I have a Backbone View vA, backed by a model mA (as it should be ) and the server side is in Spring MVC having annotated Spring controller methods with #RequestBody and #ResponseBody. I've got Jackson working fine with Spring.
Now, in the app,
Backbone.Model
|_ BaseModel (custom base model for our app)
|_ mA (my model)
mA has its own endpoint and it Backbone sucessfully calls that when making a PUT request i.e., when I call save() from a submit button event handler from View vA like so:
this.model.save({
success : function(){
alert('Request submitted successfully');
},
error : function(){
alert('Something awful happened.');
}
});
Our BaseModel has the following:
define([], function() {
window.BaseModel = Backbone.Model.extend({
......
});
onSyncError : function(model, response) {
switch (response.status) {
case 403:
[...//some more code ]
default:
alert(bundle.getDefault('HTTP_RESP_OTH') + response.status);
}
},
onSyncSuccess : function(model, response) {
alert('Sync done! ');
},
sync : function(method, model, options) {
options.error = this.onSyncError;
Backbone.sync.call(this, method, model, options);
....//some more stuff.
},
}
Spring controller method:
#RequestMapping(value="/resource/xyz/{id}.json", method = RequestMethod.PUT, consumes = {"application/json"}
, produces = {"application/json"})
#ResponseBody
public Map<String,String> methodX(#RequestBody XyzDTO xyzDTO){
....
map.put("msg", "success");
return map;
}
Also, right before I make the save call, I modify a few Model attributes, since the server side DTO has a different structure like so:
this.model.unset("abc",{ silent: true });
this.model.set( { abc: {id : "2",xyz:{ ... //more code } );
The issue is, calling save() generates a PUT request and successfully calls the Spring endpoint handler, but I get a response code 200 (which is what I expect),
but when I trace the call with Firebug, it goes into the onSyncError method and gives me an error message (because of the "default" case in it).
The Backbone doc says : "When returning a JSON response, send down the attributes of the model that have been changed by the server, and need to be updated on the client". Well, I don't need to update the model on the client side, its one of the last screens and I just need to tell the user of a success / error and
redirect him to a main page/dashboard.
I read up some more, and it seems code 200 as response is not sufficient - there might be JSON parsing errors causing the sync to fail.
I checked the response in Firebug, and the response JSON looks like {"msg":"Success"}.
So, what could be going wrong?

Backbone.Model.save() expects the response from the server to be an updated hash of the model's values. If your response is of the kind {"msg":"Success"}, Backbone may fail to sync with your model. Basically, it interprets your HTTP 200 JSON response as the model's attributes and tries to sync the model accordingly.
You might try either 1) Making your Spring controller path return a JSON-ified model response, 2) Return a plain 200 with an empty response body or 3) write a custom parse method which looks for responses with the {"msg":"Success"} format and responds differently.

Thanks for your time. I was finally able to get around the problem by using $.ajax to make the PUT request, thereby bypassing the whole Backbone sync thingy. My success handler in the ajax callback handles the response and there are no more sync errors (since its not being called anyways) :)

I'll share my experience with the same problem;
custom base-model and
calling model.save and no success event fired.
My problem was with a custom set function in the base model which didnt return "this".
If you peek at the backbone source code for model-save you'll find this snippet:
options.success = function(resp) {
// Ensure attributes are restored during synchronous saves.
model.attributes = attributes;
var serverAttrs = model.parse(resp, options);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return false;
}
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
The !model.set(serverAttrs, options) failed in my case and the save-function returned false before triggering any events.
Maybe this wasn't your problem but hopefully it'll help someone else out there...

Validate your JSON response..
In my case I had an extra comma (,)..
Nearly in-valid response may cause this issues

Related

node.js and hapi: fetching data from a database synchronously

Coming from a .net world where synchronicity is a given I can query my data from a back end source such as a database, lucene, or even another API, I'm having a trouble finding a good sample of this for node.js where async is the norm.
The issue I'm having is that a client is making an API call to my hapi server, and from there I need to take in the parameters and form an Elasticsearch query to call, using the request library, and then wait for the instance to return before populating my view and sending it back to the client, problem being is that the request library uses a callback once the data is returned, and the empty view has long been returned to the client by then.
Attempting to place the return within the call back doesn't work since the EOF for the javascript was already hit and null returned in it's place, what is the best way to retrieve data within a service call?
EX:
var request = require('request');
var options = {
url: 'localhost:9200',
path: {params},
body: {
{params}
}
}
request.get(options, function(error, response){
// do data manipulation and set view data
}
// generate the view and return the view to be sent back to client
Wrap request call in your hapi handler by nesting callbacks so that the async tasks execute in the correct logic order. Pseudo hapi handler code is as following
function (request, reply) {
Elasticsearch.query((err, results) => {
if (err) {
return reply('Error occurred getting info from Elasticsearch')
}
//data is available for view
});
}
As I said earlier in your last question, use hapi's pre handlers to help you do async tasks before replying to your client. See docs here for more info. Also use wreck instead of request it is more robust and simpler to use

Returning an Error to Ajax from MVC - Firewall blocking

I am building a MVC application, and we are using some Ajax calls to an MVC Action like this:
$.ajax({
url: myController/MyAction/1, context: document.body,
success: function (data) {
$('.modal-title').html(title);
$('.modal-body').html(data);
$(this).addClass("done");
$('#myModal').modal('show');
},
error: function (err, status, a, b) { toastr.error(a) }
});
When everything is OK, the Action returns html data and fills the modal body with HTML.
When something goes wrong, it returns status code 400 with a custom message and displays it with toastr (.js - a nice colourful alert box)
Here's the MVC Action called:
public ActionResult MyAction(string id)
{
var viewModel = new partialViewModel();
if (!string.IsNullOrEmpty(id))
{
var data = Get_Html(id); // do something, get the html
if(data == null)
{
// something is wrong, return status code 400 with my custom message
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "My error message.");
}
viewModel.Data = data; // fill the viewModel, the partial view is using
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "My error message.");
}
// return the partial view filled with data as Html
return PartialView("_myPartialView", viewModel);
}
I was inspired by these StackOverflow answers: link 1 and link 2
Everything worked fine on DEV PCs, but then we released the application to the customer's server...
The customer's server security is quite high, so he uses gateways, which scan the responses the app returns.
When something goes wrong (server returns status code 400 and my message to the client), and goes wrong often (twice per sec. for every logged in user), there's a possible scenario, that the gateways could recognize legit requests, which return legit status 400 as DoS attacks and block the poor user's IP.
Another possible scenario is, that one of the gateways can catch my 400, throw it away and return its own custom error with some other status code (and without my custom message!)
Currently I decided to solve it, by returning statusCode 200 (OK) with a special statusText and then inside the Ajax success function determine if there's this special text and show messages:
//... ajax call ...
success: function (htmlData, a, b) {
if (b.statusText.indexOf('INT_ERROR:') == 0) {
toastr.error(b.statusText.replace('INT_ERROR:', ''));
}
else {
// fill and show the modal
}
},
//... the rest ...
But that's not a good solution. Does somebody know a better way? I can not persuade the customer to change his security.
You may also tell me, it IS a good solution. I just don't like it.
Thanks

AngularJS: Cancel overwriting values $resource object after calling save ();

var User = $resource(
'http://test/index.php'
);
var user = User.get({id:'1'});
// GET: http://test/index.php?id=1
// server returns: { "login":"foo", "name":"bar", "mail":"baz" }
user.name = "qux";
user.$save();
// POST: http://test/index.php?id=1
// server returns: { "login":"foo", "name":"bar", "mail":"qux"}
In this case, when you call the save() user object, properties will be replaced by those that came from the server.
But if the server responds like this:
{
"errors":{
"login":"too short",
"name":"is already using that name.",
"mail":"invalid email."
}
}
User object properties are overwritten and instead, property errors containing these mistakes will come up.
Is there a way to change the behavior of $resource? I would like to check the status of the response and, based on that, decide whether to update the properties of an object or report an error to the user.
Angular's $resource is meant to interact with RESTful web services.
In RESTful web services, if there's an error while saving a resource, you should return an appropriate HTTP status (for example, 400).
Then, you can optionally use the error callback:
user.$save(function (response) {
console.log("success!");
}, function (response) {
console.log("error");
});
For a full list of error HTTP statuses:
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error

Backbone.js with express.js - sync using only POST and GET

My app is Backbone.js for client-side, Express.js for back-end.
I have problems with syncing with all parts of my API, using the backbone model and collection(they use urlRoot: "/users").
I'm allowed to use only GET or POST, no PUT or DELETE.
*I'm not allowed to use more models*
Not allowed to use jQuery ajax
My API
add new user:
I need to make a POST to /users with JSON of new data. So I did it just fine with - this.model.save({new data...})
list all users:
My API for that, responses to GET /users, with the right handler - so, this.collection.fetch() - works fine.
Log-In:
My API accepts POST to /users/login for that. How can I add a function "logIn" to my model, that will use custom sync/pass options.url to sync - or any other way - that will POST to /users/login ?
Log-Out:
API accepts POST to /users/logout - how to send this request using my backbone model ?
User By ID:
Same question here for GET /users/:id
Update user:
POST /users/:id - same question again.
--- So actually, the question in short is ---
What is the best way (or the most "right"), to implement methods of a backbone model, that are similar to "model.save()" - but just POST/GET to a bit different path then urlRoot ?
You probably have a couple options here. One would be structuring your models in a way that supports the urls you want. For instance, have a User model and a Session model that deal with updating the user and managing the logged in state separately.
The other thing you should probably do is to use the url method in your models.
Something like this in your User model. (Note: using urlRoot instead of url here is identical, but this is the correct approach for anything more complicated that is needed in the url)
url : function() {
var base = "/users/";
if(this.isNew()) {
return base;
} else {
return base + this.get("id");
}
}
You could extend this same concept to work in your Session model for handling logout vs login based on if the Session is new or not.
Update:
If you must use the same model, the best thing would be to totally bypass the Backbone.sync method and write a custom AJAX call with success/error handlers that know how to clean things up.
login : function() {
$.post("/users/login", {success: function (response) {
// Update user as needed
}, error: function (xhr, response) {
// Handle errors
}
}
}

What does a Backbone.js 'Model' expect back from the server after a call to Save()?

What does a Backbone.js 'Model' expect back from the server after a call to Save()?
The reason I ask is because I had an issue with the model getting cleared after a call to Save(). I fiddled around with the model's parse() method. I found the returning an empty object or null did not produce this behavior. However, the server was returning some JSON as a "success" message and this information seemed to be overwriting the model:
{
message: 'SUCCESS'
}
What is the "correct" way for the server to respond to a Save() request from a Backbone model?
Thanks!
The server should responde with an HTTP status of 200, most likely, and should return any data that the server generates or updates for the model. Typically, this is only the model's server generated id field. Any fields that are returned from the server will be applied to the model. So if you send this to the server:
{
foo: "bar"
}
and the server response with this:
{
id: 1, // or some other server generated ID, from a database, etc.
message: "SUCCESS"
}
Your model will end up looking like this:
{
id: 1,
foo: "bar",
message: "SUCCESS"
}
If your model does not need any data updated from the server when the save method is called, then you should return an empty {} object literal.
Well this question often pop's out here and I have already asked it at SO. Seems adding this to comments is not going to help future questioner hence here is the link to answer and question
Getting started with backbonejs - what should a server return

Categories