Let's say, I have a class like this:
var block = class {
constructor(val1, val2) {
this.value1 = val1;
this.value2 = val2;
}
}
I want to send an instance of this class from one server to another. I would typically pass it through JSON.stringify() before sending it to the other server, then JSON.parse() once it reaches the other server. However, this would involve converting it to JSON and it would no longer be an instance of the 'block' class.
If I wanted to send it over in a way that keeps it as an instance of the 'block' class what would be best practice.
What your asking for here, is a kind of RPC (Remote procedure call).
And I think your right in making it so that when you deserialize the object at the server side, you can re-use the class that you used at client side.
Using your example class block, here I've added a method called display,. What's handy here is that display will work on both servers after sending the JSON.
Obviously here in an SO snippet it's a little awkward to show Server to Server comms, but what I have done is save to a String, and then regenerate the same class from the String, exactly what you would do if doing some RPC.
You could of course extend this so that even the class name gets sent, and this is used to re-generate the object, so that it could send lots of types of RPC commands.
var block = class {
constructor(val1, val2) {
this.value1 = val1;
this.value2 = val2;
}
display() {
console.log(this.value1, this.value2);
}
}
var b1 = new block(1,2);
b1.display();
//lets pretend our stream is what gets sent over network
var stream = JSON.stringify(b1);
var b2 = new block();
Object.assign(b2, JSON.parse(stream));
b2.display();
Related
I use jquery.get() to make a call to the Spring-Boot backend, like this:
var url = "http://localhost:8080/api/v1/get_holdings?userId=1";
$.get(url, function(payload,status) {
if (status == "success") {
$.each(payload.data, function (key,entry) {
...
})
}
else {...
In SpringBoot I create a model that has an embedded model. In this case the model is called Holding and the embedded model is called Account, like this:
#Entity
#Table(name="holding")
public class Holding {
...
#ManyToOne //Many holdings to One Account
#JoinColumn(name="account_id", nullable=false)
private Account account;
...
//getters and setters...
The call to the database returns the data correctly.
Then I try to return it to the jquery ajax call like this:
JsonResponse response = new JsonResponse();
List<Holding> holdings = holdingRepo.getUserHoldings(userId);
response.setData(holdings); //response looks correct but browser shows a 500 error. Why?
return response;
JsonResponse is just a wrapper that I use for all my responses. I've used it many times and has worked fine for many other calls:
public class JsonResponse {
private String status = null;
private Object data = null;
//getters and setters...
It returns to the browser ok, but the browser gives a 500 error.
There are two ways that I can get the browser to accept the data.
create a separate model that contains just the data I need with no embedded models
create the json manually using the toString() method (pain in the butt)
I've tried converting the Holding object to JSON using Gson.toJson and JSONArray(holdings) but both of those methods fail. What am I missing?
You are using a relation. I suppose there happens an exception, like following:
org.hibernate.LazyInitializationException: could not initialize proxy
This can happen because Hibernate tries to optimize performance and does not load objects in the relation.
Then there are many answers to this question on SO.
One solution can be to force Hibernate to initialize the proxy via
Hibernate.initialize( entity )
Another approach can be not to send entities to the client, but to transform them into data transfer objects. If you do such transformation within the transaction, Hibernate will resolve all needed objects automatically and you will not need any tricks.
I'm wondering to which extend should we add in an API values that can be calculated from the raw data and extra available information (from the browser session, user interface.. or whatever)
For example, we could have an API returning this JSON:
{
ownder_id: '123',
canAddComment: true,
etc...
}
That gives us the value "canAddComment" directly.
Or we could have just this:
{
ownder_id: '123',
etc...
}
Where, comments can be calculated from the owner_id. For example, a user can add comments if the session owner_id is different from the received from the API.
We could have done this in Javascript instead:
//supposing we have a session object with our browser session values
var params = {owner_id: session.owner_id};
$.post(apiURL, params, function(data){
var result = JSON.parse(data);
//processing the data
result["canAddComments"] = result.owner_id !== session.owner_id;
//do whatever with it
});
What would be the best approach? What's the recommendation in this kind of cases?
I'm not exactly sure what you want to know. But my first reaction is you make a function out of it.
// constructor
function Comment() {
this.owner_id;
this.canAddComment = function() {
return session.user_id === this.owner_id;
}
}
var session = {
user_id: 22,
name: 'Kevin'
};
var my_comment = new Comment();
my_comment.owner_id = 15;
if(my_comment.canAddComment()) {
$.ajax({
// now read the form and submit the data ...
})
}
else {
alert('Only the author of this message can update ...');
}
EDIT:
My question was mainly if this should better be calculated in the backend side or calculated after retrieving the API data.
Not sure if there is a generic answer. A few arguments: if you want the method to be secret, you must do it on server side. In other cases, I think it makes perfect sense to let the client PC use its processing power.
Okay, one example: sorting. Let's say there is a table full of data; clicking on the head sorts the table according to that column. Does it make sense to send all the data to the server, let it sort the table and return an array of keys? No, I think it makes more sense to let the client process this.
http://tablesorter.com/docs/
Similar with Google Maps: you drag a marker to some place, then the program must calculate the closest 5 bus stops. Well, obviously you calculate all of this on client side.
I was wondering if there is a way to declare an object property as a function, but also as an object, at the same time.
I have a JavaScript program that provides a simple API that sends AJAX requests to a server. My goal is trying to make this API as simple and human-readable as possible.
Basically, I'd like to make it possible to do this:
var app = new App();
app.get.client(123) // Get client ID 123
app.get.client.list() // Get an array of all clients
app.login('username', 'password') // Send credentials to log as username/password
app.login.as('John') // Login using credentials stored in a server-side constant
I doubt that's even possible as I've never anything like it, but I can't think of a more clear and human-readable way to lay out methods. Sure would be nice!
A function’s an object too!
app.get.client = function(id) {
// Get client by ID
};
app.get.client.list = function() {
// List them
};
works as you’d expect.
Personally, though, I’d find:
app.clients.byId(123)
app.clients
app.login('username', 'password')
app.loginAs('John')
more readable.
I am making an AJAX chat room with the guidance of an AJAX book teaching me to use JSON and eval() function.
This chat room has normal chat function and a whiteboard feature.
When a normal text message comes from the php server in JSON format, the javascript in browser does this:
Without Whiteboard Command -------------------------------------------
function importServerNewMessagesSince(msgid) {
//loadText() is going to return me a JSON object from the server
//it is an array of {id, author, message}
var latest = loadText("get_messages_since.php?message=" + msgid);
var msgs = eval(latest);
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
} ...
The whiteboard drawing commands are sent by server in JSON format with special user name called "SVR_CMD", now the javascript is changed slightly:
With Whiteboard Command --------------------------------------------------
function importServerNewMessagesSince(msgid) {
//loadText() is going to return me a JSON object from the server
//it is an array of {id, author, message}
var latest = loadText("get_messages_since.php?message=" + msgid);
var msgs = eval(latest);
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
if (msg.author == "SVR_CMD") {
eval(msg.contents); // <-- Problem here ...
//I have a javascript drawLine() function to handle the whiteboard drawing
//server command sends JSON function call like this:
//"drawLine(200,345,222,333)" eval() is going to parse execute it
//It is a hacker invitation to use eval() as someone in chat room can
//insert a piece of javascript code and send it using the name SVR_CMD?
else {
displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
}
} ...
Now, if the hacker changes his username to SVR_CMD in the script, then in the message input start typing javascript code, insdead of drawLine(200,345,222,333), he is injecting redirectToMyVirusSite(). eval() will just run it for him in everyone's browser in the chat room.
So, as you can see, to let the eval to execute a command from an other client in the chat room is obviously a hacker invitation. I understand the book I followed is only meant to be an introduction to the functions. How do we do it properly with JSON in a real situation?
e.g. is there a server side php or .net function to javascriptencode/escape to make sure no hacker can send a valid piece of javascript code to other client's browser to be eval() ? Or is it safe to use JSON eval() at all, it seems to be a powerful but evil function?
Thank you,
Tom
What is this book? eval is evil, there is not a single reason to use it, ever.
To transform a JSON string into a javascript object, you can do the following:
var obj = JSON.parse(latest)
Which means you can then use:
[].forEach.call(obj, function( o ) {
// You can use o.message, o.author, etc.
} )
To do the opposite (javascript object -> JSON string), the following works:
var json = JSON.stringify(obj)
It only is unsafe if the executed code is generated by other clients and not by the server. Of course you would need to prevent anybody to use that name, though I don't understand why you would use the "author" field? Just send an object {"whiteboard":"drawLine(x,y,z)"} instead of {"author":"SVR_CMD","contents":"drawLine(x,y,z)"}.
But it is right, eval() is still an invitation for hackers. One can always send invalid data and try to influence the output more or less directly. The only way for escaping is a proper serialisation of the data you want to receive and send - the drawings data. How do you receive the whiteboard commands? There is no serverside "escape" function to make javascript code "clean" - it would always be a security hole.
I would expect a serialisation like
message = {
"author": "...", // carry the information /who/ draws
"whiteboard": {
"drawline": [200, 345, 222, 333]
}
}
so you can sanitize the commands (here: "drawline") easiliy.
The use of eval() might be OK if you have very complex commands and want to reduce the transferred data by building them serverside. Still, you need to parse and escape the received commands from other clients properly. But I'd recommend to find a solution without eval.
Setting eval issue aside, do not use field that can be filled by user - .author in your code - for authentication purposes. Add another field to your JSON message, say .is_server_command that when present, would signify special treating of message. This field is will be not depended on user input and thus wouldn't be hijacked by "hacker".
This pertains to cookies set inside a script (maybe inside a script tag).
System.Windows.Forms.HtmlDocument executes those scripts and the cookies set (like document.cookie=etc...) can be retrieved through its Cookies property.
I assume HtmlAgilityPack.HtmlDocument doesn't do this (execution). I wonder if there is an easy way to emulate the System.Windows.Forms.HtmlDocument capabilities (the cookies part).
Anyone?
When I need to use Cookies and HtmlAgilityPack together, or just create custom requests (for example, set the User-Agent property, etc), here is what I do:
Create a class that encapsulates the request/response. Let's call this class WebQuery
Have a private CookieCollection (in your case public) property inside that class
Create a method inside the class that does manually the request. The signature could be:
...
public HtmlAgilityPack.HtmlDocument GetSource(string url);
What do we need to do inside this method?
Well, using HttpWebRequest and HttpWebResponse, generate the http request manually (there are several examples of how to do this on Internet), create an instance of a HtmlDocument class using the constructor that receives an stream.
What stream do we have to use? Well, the one returned by:
httpResponse.GetResponseStream();
If you use HttpWebRequest to make the query, you can easily set the CookieContainer property of it to the variable you declared before everytime you access a new page, and that way all cookies set by the sites you access will be properly stored in the CookieContainer variable you declared in your WebQuery class, taking in count you're using only one instance of the WebQuery class.
Hope you find useful this explanation. Take in count that using this, you can do whatever you want, no matter if HtmlAgilityPack supports it or not.
I also worked with Rohit Agarwal's BrowserSession class together with HtmlAgilityPack.
But for me subsequent calls of the "Get-function" didn't work, because every time new cookies have been set.
That's why I added some functions by my own. (My solution is far a way from beeing perfect - it's just a quick and dirty fix) But for me it worked and if you don't want to spent a lot of time in investigating BrowserSession class here is what I did:
The added/modified functions are the following:
class BrowserSession{
private bool _isPost;
private HtmlDocument _htmlDoc;
public CookieContainer cookiePot; //<- This is the new CookieContainer
...
public string Get2(string url)
{
HtmlWeb web = new HtmlWeb();
web.UseCookies = true;
web.PreRequest = new HtmlWeb.PreRequestHandler(OnPreRequest2);
web.PostResponse = new HtmlWeb.PostResponseHandler(OnAfterResponse2);
HtmlDocument doc = web.Load(url);
return doc.DocumentNode.InnerHtml;
}
public bool OnPreRequest2(HttpWebRequest request)
{
request.CookieContainer = cookiePot;
return true;
}
protected void OnAfterResponse2(HttpWebRequest request, HttpWebResponse response)
{
//do nothing
}
private void SaveCookiesFrom(HttpWebResponse response)
{
if ((response.Cookies.Count > 0))
{
if (Cookies == null)
{
Cookies = new CookieCollection();
}
Cookies.Add(response.Cookies);
cookiePot.Add(Cookies); //-> add the Cookies to the cookiePot
}
}
What it does: It basically saves the cookies from the initial "Post-Response" and adds the same CookieContainer to the request called later. I do not fully understand why it was not working in the initial version because it somehow does the same in the AddCookiesTo-function. (if (Cookies != null && Cookies.Count > 0) request.CookieContainer.Add(Cookies);)
Anyhow, with these added functions it should work fine now.
It can be used like this:
//initial "Login-procedure"
BrowserSession b = new BrowserSession();
b.Get("http://www.blablubb/login.php");
b.FormElements["username"] = "yourusername";
b.FormElements["password"] = "yourpass";
string response = b.Post("http://www.blablubb/login.php");
all subsequent calls should use:
response = b.Get2("http://www.blablubb/secondpageyouwannabrowseto");
response = b.Get2("http://www.blablubb/thirdpageyouwannabrowseto");
...
I hope it helps when you're facing the same problem.