This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 15.8, “How to access POST request data with Scalatra.”
Problem
You want to write a Scalatra web service method to handle POST
data, such as handling JSON data sent as a POST
request.
Solution
To handle a POST
request, write a post method in your Scalatra servlet, specifying the URI the method should listen at:
post("/saveJsonStock") { val jsonString = request.body // deserialize the JSON ... }
As shown, access the data that’s passed to the POST
request by calling the request.body
method.
The Discussion shows an example of how to process JSON data received in a post
method, and two clients you can use to test a post
method: a Scala client, and a command-line client that uses the Unix curl
command.
Discussion
Recipe 15.3 shows how to convert a JSON string into a Scala object using the Lift-JSON library, in a process known as deserialization. In a Scalatra post
method, you access a JSON string that has been POST
ed to your method by calling request.body
. Once you have that string, deserialize it using the approach shown in Recipe 15.3.
For instance, the post
method in the following StockServlet
shows how to convert the JSON string it receives as a POST
request and deserialize it into a Stock
object. The comments in the code explain each step:
package com.alvinalexander.app import org.scalatra._ import scalate.ScalateSupport import net.liftweb.json._ class StockServlet extends MyScalatraWebAppStack { /** * Expects an incoming JSON string like this: * {"symbol":"GOOG","price":"600.00"} */ post("/saveJsonStock") { // get the POST request data val jsonString = request.body // needed for Lift-JSON implicit val formats = DefaultFormats // convert the JSON string to a JValue object val jValue = parse(jsonString) // deserialize the string into a Stock object val stock = jValue.extract[Stock] // for debugging println(stock) // you can send information back to the client // in the response header response.addHeader("ACK", "GOT IT") } } // a simple Stock class class Stock (var symbol: String, var price: Double) { override def toString = symbol + ", " + price }
The last step to get this working is to add the Lift-JSON dependency to your project. Assuming that you created your project as an SBT project as shown in Recipe 15.1, add this dependency to the libraryDependencies
declared in the project/build.scala file in your project:
"net.liftweb" %% "lift-json" % "2.5+"
Test the POST
method with Scala code
As shown in the code comments, the post
method expects a JSON string with this form:
{"symbol":"GOOG","price":600.00}
You can test your post
method in a variety of ways, including (a) a Scala POST
client or (b) a simple shell script. The following PostTester
object shows how to test the post
method with a Scala client:
import net.liftweb.json._ import net.liftweb.json.Serialization.write import org.apache.http.client.methods.HttpPost import org.apache.http.entity.StringEntity import org.apache.http.impl.client.DefaultHttpClient object PostTester extends App { // create a Stock and convert it to a JSON string val stock = new Stock("AAPL", 500.00) implicit val formats = DefaultFormats val stockAsJsonString = write(stock) // add the JSON string as a StringEntity to a POST request val post = new HttpPost("http://localhost:8080/stocks/saveJsonStock") post.setHeader("Content-type", "application/json") post.setEntity(new StringEntity(stockAsJsonString)) // send the POST request val response = (new DefaultHttpClient).execute(post) // print the response println("--- HEADERS ---") response.getAllHeaders.foreach(arg => println(arg)) } class Stock (var symbol: String, var price: Double)
The code starts by creating a Stock
object and converting the object to a JSON string using Lift-JSON. It then uses the methods of the Apache HttpClient library to send the JSON string as a POST
request: it creates an HttpPost
object, sets the header content type, then wraps the JSON string as a StringEntity
object before sending the POST
request and waiting for the response.
When this test object is run against the Scalatra saveJsonStock
method, it results in the following output:
--- HEADERS --- ACK: GOT IT Content-Type: text/html;charset=UTF-8 Content-Length: 0 Server: Jetty(8.1.8.v20121106)
Note that it receives the ACK message that was returned by the Scalatra post
method. This isn’t required, but it gives the client a way to confirm that the data was properly received and processed by the server method (or that it failed).
Test the POST
method with a curl
command
Another way to test the post
method is with a Unix shell script. The following curl
command sets the Content-type header, and sends a sample JSON string to the Scalatra StockServlet
post
method as a POST
request:
curl \ --header "Content-type: application/json" \ --request POST \ --data '{"symbol":"GOOG", "price":600.00}' \ http://localhost:8080/stocks/saveJsonStock
On Unix systems, save this command to a file named postJson.sh, and then make it executable:
$ chmod +x postJson.sh
Then run it to test your Scalatra web service:
$ ./postJson.sh
You won’t see any output from this command, but you should see the correct debugging output printed by the StockServlet
in its output window. Assuming that you’re running your Scalatra web service using SBT, the debug output will appear there.
Notes
Recent versions of Scalatra use the Json4s library to deserialize JSON. This library is currently based on Lift-JSON, so the deserialization code will be similar, if not exactly the same. Either library will have to be added as a dependency.
The other important parts about this recipe are:
- Knowing to use the
post
method to handle aPOST
request - Using
request.body
to get thePOST
data - Using
response.addHeader("ACK", "GOT IT")
to return a success or failure message to the client (though this is optional) - Having
POST
request client programs you can use
The Scala Cookbook
This tutorial is sponsored by the Scala Cookbook, which I wrote for O’Reilly:
You can find the Scala Cookbook at these locations: