-
Notifications
You must be signed in to change notification settings - Fork 0
/
json_intro.html
343 lines (281 loc) · 14.2 KB
/
json_intro.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta name="keywords" content="Yaws"/>
<title>Yaws</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" type="text/css" href="stil.css"/>
<link rel="shortcut icon" href="/icons/yaws_y.gif" type="image/x-icon"/>
</head>
<body>
<div class="logo">
<img src="icons/yaws_head.gif" width="600" alt="YAWS"/>
</div>
<div id="sidebar">
<h4> Yaws </h4>
<div class=""> <a href="index.html" id="index" >Top Page</a> </div>
<div class=""> <a href="configuration.html" id="configuration">Build Config and Run</a></div>
<div class=""> <a href="dynamic.html" id="dynamic" >Dynamic Content</a> </div>
<div class=""> <a href="https://github.com/erlyaws/yaws/releases/" id="download">Download </a> </div>
<div class=""> <a href="contact.html" id="contact">Contact </a> </div>
<div class=""> <a href="doc.html" id="doc">Documentation</a> </div>
<div class=""> <a href="articles.html" id="resources">Articles</a> </div>
<h4> Examples </h4>
<div class="choosen"> <a href="/json_intro.html">AJAX/JSON RPC</a></div>
<div class=""> <a href="/appmods.html">Appmods</a> </div>
<div class=""> <a href="/arg.html">Arg</a> </div>
<div class=""> <a href="/privbind.html">Binding to Privileged Ports</a></div>
<div class=""> <a href="/bindings.html">Bindings</a> </div>
<div class=""> <a href="/cgi.html">CGI</a></div>
<div class=""> <a href="/session.html">Cookie Sessions</a> </div>
<div class=""> <a href="/cookies.html">Cookies</a> </div>
<div class=""> <a href="/dynamic.html">Dynamic Content</a> </div>
<div class=""> <a href="/embed.html">Embedding Yaws</a></div>
<div class=""> <a href="/upload0.html">File Upload</a> </div>
<div class=""> <a href="/form.html">Forms</a> </div>
<div class=""> <a href="/haxe_intro.html">haXe Remoting</a></div>
<div class=""> <a href="/pcookie.html">Persistent Cookies</a> </div>
<div class=""> <a href="/query.html">Query Part of URL</a></div>
<div class=""> <a href="/rebar_release.html">Rebar Releases</a></div>
<div class=""> <a href="/redirect.html">Redirect</a> </div>
<div class=""> <a href="/server_sent_events.html">Server-Sent Events</a> </div>
<div class=""> <a href="/ssi.html">Server Side Includes</a> </div>
<div class=""> <a href="/simple.html">Simple</a> </div>
<div class=""> <a href="/soap_intro.html">SOAP with Yaws</a></div>
<div class=""> <a href="/stream.html">Streaming Data</a> </div>
<div class=""> <a href="/websockets.html">Web Sockets</a> </div>
<a href="/shoppingcart/index.html">Tiny Shopping Cart</a>
<div class=""> <a href="/yapp_intro.html">Yaws Applications (yapps)</a></div>
<div class=""> <a href="/logger_mod.html">Write Your Own Logger</a></div>
<h4> Misc </h4>
<div class=""> <a href="/internals.html">Internals</a> </div>
</div>
<div id="entry">
<h1>AJAX through JSON RPC</h1>
<p><i>Note: this documentation used to refer to the module 'yaws_jsonrpc', but that module was deprecated in favor of 'yaws_rpc', which handles JSON RPC, haXe and SOAP remoting. For more specific information about SOAP, refer to <a href="/soap_intro.html">the SOAP page.</a></i></p>
<p>The Yaws JSON-RPC binding is a way to have JavaScript code in the browser evaluate a remote procedure call (RPC) in the Yaws server. JSON itself as described at <a href="https://www.json.org/">https://www.json.org/ </a>is basically a simple marshaling format which can be used from a variety of different programming languages, and naturally it's completely straightforward to implement in JavaScript itself. JSON-RPC version 2.0, the version Yaws supports, is described here:</p>
<p><a href="https://groups.google.com/group/json-rpc/web/json-rpc-2-0">https://groups.google.com/group/json-rpc/web/json-rpc-2-0</a></p>
<p>The Yaws JSON-RPC implementation consist of JavaScript clients and a server side library that must be explicitly invoked by Erlang code in a .yaws page, appmod, etc.</p>
<p>It is not particularly easy to show and explain an AJAX setup through JSON-RPC, but here is an attempt:</p>
<p>First we have an HTML page which:</p>
<ol>
<li>
<p>Includes the client side of the JSON library. The library is included in the Yaws distribution and it is found under <a href="https://github.com/erlyaws/yaws/blob/master/www/jsolait/jsolait.js">"www/jsolait/jsolait.js"</a>.</p></li>
<li>
<p>Second, the HTML code defines the name of a method, i.e. the name of a server-side function that shall be called by the client side JavaScript code.</p></li>
<li>
<p>Finally the HTML code defines a FORM that's used to invoke the RPC. This is just a really simple example, really any JavaScript code can invoke any RPC in more interesting scenarios than submitting a form.</p></li></ol>
<p>The HTML code appears as shown below:</p>
<div class="box">
<pre><html>
<head>
<title>Testing html-json library</title>
</head>
<script src="jsolait/jsolait.js"></script>
<script>
var serviceURL = "json_sample.yaws";
var methods = [ "test1", "errortest" ];
var jsonrpc = imprt("jsonrpc");
var service = new jsonrpc.ServiceProxy(serviceURL, methods);
function test() {
try {
foo = document.getElementById('foo').value;
bar = document.getElementById('bar').value;
document.getElementById('result').innerHTML =
"<PRE>" + service.test1(foo, bar) + "</PRE>";
} catch(e) {
alert(e);
}
return false;
}
function errortest() {
try {
document.getElementById('failure').innerHTML =
"<PRE>" + service.errortest() + "</PRE>";
} catch(e) {
document.getElementById('failure').innerHTML =
"<PRE>" + e + "</PRE>";
}
return false;
}
</script>
<body>
<form action="" method="post" onSubmit="return test()">
<div id="result">
</div>
<p>
First Argument: <input id="foo" />
</p>
<p>
Second Argument: <input id="bar"/>
</p>
<p>
<input type="submit" value="Do JSON-RPC call"/>
</p>
</form>
<form action="" method="post" onSubmit="return errortest()">
<p>
<input type="submit" value="Do JSON-RPC call expected to fail"/>
</p>
<div id="failure">
</div>
</form>
</body>
</html>
</pre></div>
<p>This HTML code resides in file <a href="json_sample.html">json_sample.html</a> and it is the HTML code that is the AJAX GUI.</p>
<p>Following that we need to take a look at json_sample.yaws (shown below), which is the "serviceURL" according to the JavaScript code. This code defines the function to be called. Remember that the JavaScript code defined one method, called "test1"; this information will be passed to the serviceURL. The code looks like:</p>
<div class="box">
<pre><erl module=sample_mod>
-compile(export_all).
out(A) ->
Peer = case yaws_api:get_sslsocket(A#arg.clisock) of
{ok, SslSocket} ->
ssl:peername(SslSocket);
_ ->
inet:peername(A#arg.clisock)
end,
{ok,{IP,_}} = Peer,
A2=A#arg{state = [{ip, IP}]},
yaws_rpc:handler_session(A2, {?MODULE, counter}).
counter([{ip, IP}] = _State, {call, errortest, Value} = _Request, Session) ->
io:format("Request = ~p~n", [_Request]),
{ false, { error, "Expected failure" } };
counter([{ip, IP}] = _State, {call, test1, Value} = _Request, Session) ->
io:format("Request = ~p~n", [_Request]),
IPStr = io_lib:format("Client ip is ~p~n" , [ IP ]),
OldSession = io_lib:format("Request is: ~p~nOld session value "
"is ~p~n", [ _Request, Session ]),
case Session of
undefined -> % create new session
NewSession = 0;
10 -> % reset session after reaching 10
NewSession = undefined;
N ->
NewSession = N + 1
end,
NewVal = io_lib:format("New session value is ~p ~n", [ NewSession ]),
Str = lists:flatten([IPStr,OldSession,NewVal]),
{true, 0, NewSession, {response, Str }}.
</erl>
</pre></div>
<p>The two important lines on the server side are</p>
<ol>
<li>
<pre>yaws_rpc:handler_session(A2, {sample_mod, counter}).</pre></li>
<li>
<pre>counter([{ip, IP}] = _State, {call, test1, Value} = _Request, Session)</pre></li></ol>
<p>The first line tells Yaws to forward all JSON-RPC methods to the "counter" function in the "sample_mod" module. The second line is the head of the counter function that will be called when the client invokes a method called 'test1'. We would duplicate this line with a different name than 'test1' for each RPC function we wish to implement. Note that the first atom in the request tuple will either be 'call' or 'notification' to indicate the type of request. As per the <a href="https://groups.google.com/group/json-rpc/web/json-rpc-2-0">JSON-RPC 2.0 specification</a>, a 'call' is a regular request-reply while a 'notification' is a one-way message that does not have a corresponding reply.</p>
<p>On the client side we have</p>
<div class="box">
<pre>
var methods = [ "test1" ];
var jsonrpc = imprt("jsonrpc");
var service = new jsonrpc.ServiceProxy(serviceURL, methods);
</pre></div>
<p>This registers the Yaws page with the JSON-RPC handler and gives it a list of methods that the Yaws page can satisfy. In this case, the only method called 'test1'.</p>
<p>When we wish to return structured data, we simply let the user-defined RPC function return JSON structures such as </p>
<div class="box">
<pre>{struct, [{field1, "foo"}, {field2, "bar"}]} </pre></div>
<p> for a structure and </p>
<div class="box">
<pre>{array, ["foo", "bar"]}</pre></div>
<p>for an array. We can nest arrays and structs in each other.</p>
<p>Finally, we must stress that this example is extremely simple. In order to build a proper AJAX application in Yaws, a lot of client side work is required, all Yaws provides is the basic mechanism whereby the client side JavaScript code can RPC the web server for data which can be subsequently used to populate the DOM. Also required to build a good AJAX application is good knowledge of how the DOM in the browser works</p>
<p>The yaws_rpc:handler will also call: M:F(cookie_expire) which is expected to return a proper Cookie expire string. This makes it possible to setup the Cookie lifetime. If this callback function is non-existent, the default behaviour is to not set a cookie expiration time, i.e., it will live for this session only.</p>
<h3>One more example </h3>
<p>Here is yet another example, stolen from <a href="https://www.redhoterlang.com/entry/ac061493b201e3d1b4490cdc3f911068">Tobbe's blog.</a></p>
<h4>Setup the DOM</h4>
<p>In the file ''ex1.html'' we create the DOM with a little HTML and add some JavaScript that will talk with the Erlang server side.</p>
<div class="box">
<pre>
<html>
<head>
<script type="application/javascript"
src="/jquery-1.2.3.js"></script>
</head>
<body>
<script language="javascript" type="application/javascript">
function ex1(what) {
$.getJSON("/ex1.yaws",
{'op': "ex1", 'what': what},
function(x) {
do_ex1(what, x)
});
}
function do_ex1(what, x) {
jQuery.each(x, doit);
}
function doit() {
$('#'+this.who).html(this.what);
}
</script>
<button onclick="ex1('one')">Update one!</button>
<button onclick="ex1('two')">Update two!</button>
<button onclick="ex1('three')">Update three!</button>
<div id="one">This is one</div>
<div id="two">This is two</div>
<div id="three">This is three</div>
</body>
</html>
</pre></div>
<h4>The erlang server side</h4>
<p>This is the code that needs to be installed and execute on the server side. It nicely illustrates how to return JSON structs to the client. </p>
<div class="box">
<pre>
-module(ex1).
-export([out/1]).
out(A) ->
L = yaws_api:parse_query(A),
dispatch(lkup("op", L, false), A, L).
dispatch("ex1", A, L) ->
ex1(A, L).
ex1(_A, L) ->
J = json2:encode(array(what(lkup("what", L, false)))),
return_json(J).
what("one") -> one();
what("two") -> one() ++ two();
what("three") -> one() ++ two() ++ three().
array(L) -> {array, L}.
one() -> obj("one").
two() -> obj("two").
three() -> obj("three").
obj(M) ->
obj(M, "r").
%%%
%%% How ::= "r" | "a" , r=replace, a=append
%%%
obj(M, How) ->
C = now2str(),
[{struct,
[{"who", M},
{"how", How},
{"what", C ++" "++M++" content"}]}].
return_json(Json) ->
{content,
"application/json; charset=iso-8859-1",
Json}.
now2str() ->
{A,B,C} = erlang:now(),
i2l(A)++"-"++i2l(B)++"-"++i2l(C).
i2l(I) when is_integer(I) -> integer_to_list(I);
i2l(L) when is_list(L) -> L.
lkup(Key, List, Def) ->
case lists:keysearch(Key, 1, List) of
{value,{_,Value}} -> Value;
_ -> Def
end.
</pre></div>
<h2>The json library</h2>
<p>The Yaws JSON library contains 3 simple functions, one for encoding and two for decoding. See source code <a href="https://github.com/erlyaws/yaws/blob/master/src/json2.erl">json2.erl</a> for detailed instructions on usage.</p></div><div class="logo">
<img src="/icons/yaws_pb.gif" alt="pbyaws" />
</div>
<p>
<a href="https://validator.w3.org/check?uri=referer"><img
src="https://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0!" height="31" width="88" /></a>
</p>
</body>
</html>