RESTful on Play! framework
Asked Answered
S

6

117

We are planning a project primarily serving content to mobile apps, but need to have a website.

My question is whether is makes sense to use Jersey or Restlet to develop REST APIs for our mobile apps, and then use Play! to serve the website.

Or does it make more sense to just use Play! to do it all? If so, how to do REST with Play! framework?

Sibeal answered 7/12, 2010 at 17:12 Comment(0)
K
112

As per request, a simple REST-like approach. It works almost the same way Codemwncis' solution works but uses the Accept header for content negotiation. First the routes file:

GET     /user/{id}            Application.user
POST    /user/                Application.createUser
PUT     /user/{id}            Application.updateUser
DELETE  /user/{id}            Application.deleteUser

You don't specify any content type here. Doing so is IMHO only necessary when you want to have "special" URIs for certain resources. Like declaring a route to /users/feed/ to always return in Atom/RSS.

The Application controller looks like this:

public static void createUser(User newUser) {
    newUser.save();
    user(newUser.id);
}

public static void updateUser(Long id, User user) {
    User dbUser = User.findById(id);
    dbUser.updateDetails(user); // some model logic you would write to do a safe merge
    dbUser.save();
    user(id);
}

public static void deleteUser(Long id) {
    User.findById(id).delete();
    renderText("success");
}

public static void user(Long id)  {
    User user = User.findById(id)
    render(user);
}

As you can see I only removed the getUserJSON method and renamed the getUser method. For different content types to work you now have to create several templates. One for each desired content type. For example:

user.xml:

<users>
  <user>
    <name>${user.name}</name>
    . . .
  </user>
</users>

user.json:

{
  "name": "${user.name}",
  "id": "${user.id}",
  . . . 
}

user.html:

<html>...</html>

This approach gives browsers always the HTML view, since all browsers send a text/html content type in their Accept header. All other clients (possibly some JavaScript-based AJAX requests) can define their own desired content type. Using jQuerys ajax() method you could do the following:

$.ajax({
  url: @{Application.user(1)},
  dataType: json,
  success: function(data) {
    . . . 
  }
});

Which should get you the details about User with the ID 1 in JSON format. Play currently supports HTML, JSON and XML natively but you can easily use a different type by either following the official documentation or use the content negotiation module.

If you are using Eclipse for development I suggest use the REST client plugin which lets you test your routes and their corresponding content type.

Kr answered 13/12, 2010 at 18:30 Comment(6)
Thanks for posting this. The Play! docs are some of the best I've seen for explaining the basic structure of things, but are occasionally lacking in detailed examples. Having the two approaches demonstrated on the same example really clears things up.Agnesse
I am trying out your example, I am curious where the posted JSON data is converted to User class. for example, inside the createUser function I find the newUser is null.Sibeal
@Gary: Maybe you used "user" instead of "newUser"? The name of the controller and form parameter must match. I've created a simple project that shows the above method, including HTML/XML/JSON output for all users at github.com/sebhoss/play-user-sampleKr
Thanks, I tested it by using curl to send JSON string, and it appears that play framework did not recognize the application/json content type: groups.google.com/group/play-framework/browse_thread/thread/…Sibeal
@Gary: Thanks for the hint! It seems it is fixed in the master branch, you could try building it yourself and then test again..Kr
Is this solution still valid in Play 2.0?Stephniestepladder
C
68

This is still a popular question, but the highest voted answers are not up to date with the current version of play. Here's a working REST example with play 2.2.1:

conf/routes:

GET     /users                 controllers.UserController.getUsers
GET     /users/:id             controllers.UserController.getUser(id: Long)
POST    /users                 controllers.UserController.createUser
PUT     /users/:id             controllers.UserController.updateUser(id: Long)
DELETE  /users/:id             controllers.UserController.deleteUser(id: Long)

app/controllers/UserController.java:

public static Result getUsers()
{
    List<User> users = Database.getUsers();
    return ok(Json.toJson(users));
}

public static Result getUser(Long id)
{
    User user = Database.getUser(id);
    return user == null ? notFound() : ok(Json.toJson(user));
}

public static Result createUser()
{
    User newUser = Json.fromJson(request().body().asJson(), User.class);
    User inserted = Database.addUser(newUser);
    return created(Json.toJson(inserted));
}

public static Result updateUser(Long id)
{
    User user = Json.fromJson(request().body().asJson(), User.class);
    User updated = Database.updateUser(id, user);
    return ok(Json.toJson(updated));
}

public static Result deleteUser(Long id)
{
    Database.deleteUser(id);
    return noContent(); // https://mcmap.net/q/45071/-http-status-code-for-update-and-delete
}
Coinsurance answered 13/11, 2013 at 15:1 Comment(1)
I also would like to see an updated version of seb's Answer, but unfortunetely your answer removed all the .xml and .html magic. :-(Roseannroseanna
I
26

Use Play! to do it all. Writing REST services in Play is very very easy.

Firstly, the routes file makes it straightforward to write routes that conform to the REST approach.

Then, you write your actions, in the controller, for each API method you want to create.

Depending on how you want to return the result (XML, JSON etc), there are a few methods you can use. For example, using the renderJSON method, allows the results to be rendered very easily. If you want to render XML, then you can just do so in the same way as you would build an HTML document in your View.

Here is a neat example.

routes file

GET     /user/{id}            Application.getUser(format:'xml')
GET     /user/{id}/json       Application.getUserJSON
POST    /user/                Application.createUser
PUT     /user/{id}            Application.updateUser
DELETE  /user/{id}            Application.deleteUser

Application file

public static void createUser(User newUser) {
    newUser.save();
    renderText("success");
}

public static void updateUser(Long id, User user) {
    User dbUser = User.findById(id);
    dbUser.updateDetails(user); // some model logic you would write to do a safe merge
    dbUser.save();
    renderText("success");
}

public static void deleteUser(Long id) {
    // first check authority
    User.findById(id).delete();
    renderText("success");
}

public static void getUser(Long id)  {
    User user = User.findById(id)
    renderJSON(user);
}

public static void getUserJSON(Long id) {
    User user = User.findById(id)
    renderJSON(user);
}

getUser.xml file

<user>
   <name>${user.name}</name>
   <dob>${user.dob}</dob>
   .... etc etc
</user>
Impermeable answered 7/12, 2010 at 20:44 Comment(8)
Is it possible to pick the right getUser method based on the Accept header?Sherrellsherrer
it is, but not entirely reliable. If play knows that the header is a JSON request, then it will try to render a getuser.json file. If the header is an xml, then it will try getuser.xml. However, it is much easier to understand, and more REST like, to user /User/{id}/typeImpermeable
I don't think it is more REST-like to specify the representation type explicitly in the URI. It is better to use the Accept header directly and do not change the URI as the resource you want to see remains the same. The example above could be rewritten to have just a single getUser(Long id) method which does exactly the same as its current implementation but instead of defining a getUserJSON, getUserXML, etc. you rather define a getUser.json and getUser.xml template. Although I'd rename that to user.json/user.xml tooKr
Thanks, this is very helpful. Appreciate it!Sibeal
@Kr - can you expand your comment into an answer? I'd love to see an example of the technique you describeAgnesse
@Codemwnc Can you please help me on this https://mcmap.net/q/189259/-play-framework-pass-multiple-images-in-a-post-request/1584121Alejandro
hello, please help me in this question, i am having a problem #32699920Curnin
Can this service be consumed by Java Swing client ?Ozalid
D
5

Integrating with a JAX-RS implementation is a possible alternative approach to using Play's built-in HTTP routing. For a RESTEasy example, see the RESTEasy Play! module.

This approach makes sense if you are already invested in JAX-RS, or if you need some of the advanced features REST that JAX-RS provides such as content negotiation. If not, it would be simpler to just use Play directly to serve JSON or XML in response to HTTP requests.

Deflation answered 9/12, 2010 at 21:17 Comment(0)
R
4

you should have a look at

http://www.lunatech-labs.com/open-source/resteasy-crud-play-module

it's a module for play that automatically build a rest interface, just like the crud module automatically builds an admin area...

Render answered 17/2, 2011 at 4:36 Comment(0)
H
2

It does seem like this approach is broken in Play version 1.2.3. If you download the source done by @seb and mentioned earlier https://github.com/sebhoss/play-user-sample, the creation of a new user object using POST with a JSON object is no longer possible.

You need to have specific methods for creation done using with json and xml POSTs. Outlined here: https://groups.google.com/forum/#!topic/play-framework/huwtC3YZDlU

Hbeam answered 22/11, 2011 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.