Building a simple RESTful api in Play framework
Bài đăng này đã không được cập nhật trong 8 năm
Recently I have started working in a project where we have to develop RESTful API with Play framework. There are number of reasons why we've chosen Play framework over others. Mostly it was an architectural decision due to nature of the application we will be building. Before I start explaining how to build a RESTful api and setting things up in the process, a brief explanation about benefits of this framework will be fitting.
Why Play Framework?
- It leverages the JVM, a popular and mature execution environment, and all the libraries of the Java ecosystem.
- It caters to a large developer population, spanning enterprise software, academia and startups.
- It enforces type safety and encourages object-oriented design.
- It’s a great choice for API-first (RESTful) design.
- Play’s stateless architecture enables horizontal scaling, ideal for serving many incoming requests without having to share resources (such as a session) between them.
- It’s at the forefront of the Reactive programming trend, in which servers are event-based and parallel processing is used to cater to the ever-increasing demands of modern websites.
Setting up a Play! project
We have to download latest play release provided by LightBend which includes activator. Unzip in a directory of your choice. We’ll refer to uncompressed folder directory as PLAY_HOME.
Add PLAY_HOME folder to you PATH environment variable, so that it’s handy when you run play commands from within your project. If you’re using Bash, you can do this by simply appending this to bashrc.
export PATH=$PATH:/home/rofi/play-2.5.9
Lets create a new project firing up the console like this.
➜ rest-play-java git:(master) play new rest-play-java
and then type run to execute the project. At this stage, dependency jars specified in build.scala and other play dependencies are downloaded and copied to classpath. and then we visit localhost:9000 to see that our application is ready.
Cassandra with Kundera
We will be using Cassandra as our NoSQL database of choice as we are using this in our project. Following describes the steps required to integrate kundera as our ORM with play framework project.
We have to setup Cassandra server and have it up and running. Please refer this guide to install and run Cassandra in case you haven't set it up already.
Adding dependencies
Since we will be using datastax java driver as our client driver for Cassandra, we need kundera-cassandra-ds-driver in order to use it via kundera . To add support, we need to add dependencies to build.sbt which will look like following:
libraryDependencies ++= Seq(
javaJdbc,
cache,
javaWs,
"com.datastax.cassandra" % "cassandra-driver-core" % "3.1.0",
"com.datastax.cassandra" % "cassandra-driver-mapping" % "3.1.0",
"com.datastax.cassandra" % "cassandra-driver-extras" % "3.1.0",
"com.impetus.kundera.client" % "kundera-cassandra-ds-driver" % "3.5",
filters
)
Write Persistence.xml file
Please make sure to put it under a META-INF folder in /conf directory.
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="cassandra_pu">
<provider>com.impetus.kundera.KunderaPersistence</provider>
<class>models.User</class>
<properties>
<property name="kundera.nodes" value="localhost"/>
<property name="kundera.port" value="9042"/>
<property name="kundera.keyspace" value="fsns"/>
<property name="kundera.dialect" value="cassandra"/>
<property name="kundera.client.lookup.class" value="com.impetus.kundera.client.cassandra.dsdriver.DSClientFactory" />
</properties>
</persistence-unit>
</persistence>
Make entity classes available at runtime
When running in development mode, Play uses its own Classloader that doesn’t read classes from target/scala-2.xx/classes folder. As a result, your User entity won’t be loaded at runtime throwing exceptions as a consequence. In order to make your entity classes available at run time (in development mode, of course), all you’ve to do is to create jar file out of them and put this jar file into lib folder under project root directory.
➜ rest-play-java git:(master) cd target/scala-2.11/classes \
➜ rest-play-java git:(master) jar -cvf myEntities.jar models \
➜ rest-play-java git:(master) cp myEntities.jar ../../../lib \
➜ rest-play-java git:(master) cp myEntities.jar ../../../
Start Cassandra Server and create schema/ tables
Now we create schema and tables for User entity to be written to:
➜ rest-play-java git:(master) cqlsh
Connected to Test Cluster at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 3.6 | CQL spec 3.4.2 | Native protocol v4]
Use HELP for help.
cqlsh> create keyspace KunderaExamples;
cqlsh> use KunderaExamples;
cqlsh> create column family users with comparator=UTF8Type and default_validation_class=UTF8Type and key_validation_class=UTF8Type;
81852270-2374-11e1-0000-242d50cf1fdd
Waiting for schema agreement...
With that, we setup cassandra successfully and we will start talking to it soon.
A Basic Example
Now we will expose a basic endpoint where we will store an individual user and test if it works. We don’t need to build anything special to expose a REST API — by default Play supports RESTful web services by enabling the developer to match an HTTP verb and an endpoint with a function defined in a custom controller via a flexible ‘routes’ file.
POST /users controllers.UserController.create()
The above route maps the endpoint ‘/users’ and the HTTP verb POST to the create function of the User controller. Every time an incoming HTTP request hits the server, an Action is triggered, as defined in a Controller function. To serve the request described in our routes file, all we need to do is describe the create function in the User controller:
/**
* Create an user with the data of request
*
* @return Result
*/
@Transactional
public Result create() {
Form<User> user = userForm.bindFromRequest();
if (user.hasErrors()) {
return jsonResult(badRequest(user.errorsAsJson()));
}
User newUser = UserService.create(user.get());
return jsonResult(created(Json.toJson(newUser)));
}
and now comes the moment of truth, if we fire up the console and curl the following request
curl -H "Content-Type: application/json" -X POST -d '{id": 1,"name": "ayna"}' http://localhost:9000/users
we should be able to see this.
{ "success": "201", "message": "User Successfully Created" }
And there we have it — a simple REST API that responds play-json object with appropriate http codes when the endpoint is requested. Please refer to this github link in order to see my full implementation of this project. Until next time, ciao!
All rights reserved