The default data protocol of front-end JavaScript websites is JSON. When you’re writing a back-end service, handling JSON is becoming essential. This chapter will build a JSON server in ClojureScript.
In this recipe you use Clojure to create a simple JSON server. We create a new Clojure project, create an HTML page with JavaScript to post the JSON, and create a Clojure server in Compojure to receive the request and return a response. Then we’ll process and display that response in the HTML page’s JavaScript.
In this chapter we assume the following:
You know enough JavaScript to recognize a JSON post and receive (or are prepared to Google it to find out).
You have some familiarity with the JQuery library—so we don’t need to explain JQuery references like $
.
The benefit of this chapter is that you will get the foundational skills you’ll need when building a rich client application that sends data over the wire.
Follow these steps:
1. Create a new Compojure project json-demo
.
lein new compojure json-demo
cd json-demo
2. Include the following in your projects.clj
file:
(defproject json-demo "0.1.0-SNAPSHOT"
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.7.0-beta2"]
[compojure "1.3.4"]
[ring/ring-defaults "0.1.5"]
[ring/ring-json "0.3.1"]]
:plugins [[lein-ring "0.9.5"]]
:ring {:handler json-demo.handler/app}
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.2.0"]]}})
3. Create a new file called /resources/public/postjson.html
. In the file, put the following contents:
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<form action="/a" id="jsonForm">
<input type="submit" value="Submit a JSON post" />
</form>
<!-- the result of the json test will be rendered inside this div -->
<div id="result"></div>
<script>
/* attach a submit handler to the form */
$("#jsonForm").submit(function(event) {
/* stop form from submitting normally */
event.preventDefault();
var book = {
"Title" : "The Adventures of Tom Sawyer",
"Author" : "Mark Twain",
"Year" : "1876"
};
var bookJSON = JSON.stringify(book)
/* get some values from elements on the page: */
var $form = $( this ),
url = $form.attr( 'action' );
/* Send the data using post */
var postResult = $.post( url, { s: bookJSON } );
/* Put the results in a div */
postResult.done(function( data ) {
console.log(data);
var result = "";
$(data).each(function(i,val){
$.each(val,function(k,v){
console.log(k+" : "+ v);
result += k + " : " + v + "<br>";
});
});
$( "#result" ).empty().append( result );
});
});
</script>
</body>
</html>
4. Modify the file src/json_demo/handler.clj
to contain the following:
(ns json-demo.handler
(:require [compojure.core :refer :all]
[compojure.route :refer [not-found]]
[ring.middleware.resource :refer [wrap-resource]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.json
:refer [wrap-json-body wrap-json-response]]
[ring.util.response :refer [response redirect]]))
(defn respond-to-json [s]
;used for debugging
(println "post-parameters: " s)
(response {:main-character2 "Huck"
:main-character1 "Tom"}))
(defroutes app-routes
(GET "/" []
(redirect "/postjson.html"))
(POST "/a" [s] (respond-to-json s))
(not-found (str "no matching route found
"
"Try <a href='/postjson.html'>this</a>")))
(def app
(-> app-routes
(wrap-params)
(wrap-resource "public")
(wrap-json-body)
(wrap-json-response)))
Next we test the solution.
1. On the command line, run
lein ring server 4000
Notice that a new web server window opens. (See Figure 6.1.)
2. When you click the Search button, you get what is shown in Figure 6.2.
The results of the JSON are displayed in the browser window. In the server console, the following is displayed:
post-parameters: {"Title":"The Adventures of Tom Sawyer","Author":"Mark Twain",
"Year":"1876"}
First we’ll take a look at the project.clj
file. Of particular interest is the ring.json
library and the lein-ring
library. Also noteworthy is the :ring :handler
that points to the app
var.
The ring.json
library is ring’s JSON library. The benefit of this is that you can wrap the routes and have input and output converted to JSON.
The lein-ring
plug-in enables you to start a ring server from the command line using Leiningen. This uses the :ring :handler
reference to the app
function as a hook into the code. The namespace symbol json-demo.handler/app
tells us that the var is in the handler.clj
file in the json-demo.handler
namespace.
Then we’ll take a look at the postjson.html
file. The first line we’re particularly interested in is the serialization of the JavaScript object to JSON:
var bookJSON = JSON.stringify(book)
Here we pass the JavaScript object book into a JavaScript method to convert it to JSON data. Then we send the JavaScript object to the server via a JavaScript http POST using the form information in the page, without actually submitting the whole page:
var postResult = $.post( url, { s: bookJSON } );
This will take the results of the post and store it in the var posting. If we hadn’t wrapped the response from the server in JSON we might have to use jQuery to parse the JSON response, but this has been taken care of by the server. We have a JavaScript object, postResult
, containing our JSON object tree.
Then we iterate through the map in the JSON and append it to the DOM in a div:
postResult.done(function( data ) {
console.log(data);
var result = "";
$(data).each(function(i,val){
$.each(val,function(k,v){
console.log(k+" : "+ v);
result += k + " : " + v + "<br>";
});
});
$( "#result" ).empty().append( result );
Now we’ll look at the handler.clj
file. The lines we’re particularly interested in are the handler for the JSON:
(POST "/a" [s] (respond-to-json s))
This hands the response off to the respond-to-json
method. Inside the respond-to-json
method we convert the JSON string to a Clojure map and print it:
(println "post-parameters: " s)
Then we construct a new JSON result to send back. This is a map with two entries that will get converted to a JSON string.
(response {:main-character2 "Huck"
:main-character1 "Tom"}))
Also of interest are the routes. The first (GET "/" []
route will redirect the browser to our .html page, which can lead to a better user experience. The (not-found
route will return a simple response prompting the user to go to the .html page.
(defroutes app-routes
(GET "/" []
(redirect "/postjson.html"))
(POST "/a" [s] (respond-to-json s))
(not-found (str "no matching route found
"
"Try <a href='/postjson.html'>this</a>")))
In this chapter you saw how to create an HTML file to pass JSON from JavaScript. Then we set up a Compojure server to receive the JSON post, parse it, and display it on the HTTP response.
The next things to investigate on your own are two ways to send data over the wire—using EDN and Transit. EDN is a ClojureScript alternative to JSON that allows for richer serialization and deserialization of Clojure and ClojureScript values. Transit has similar goals to EDN but makes use of the parsing performance of the JSON implementations built into the browser.
13.58.220.83