Play!框架上的RESTful

118

我们正在计划一个主要为移动应用提供内容的项目,但需要有一个网站。

我的问题是,是否使用Jersey或Restlet开发REST API为我们的移动应用程序服务,然后使用Play!来提供网站是有意义的呢?

还是只使用Play!来完成所有任务更加合适?如果是这样,如何在Play!框架中实现REST呢?

我们计划为移动应用程序提供内容,同时需要拥有一个网站。我的问题是,是否使用Jersey或Restlet来开发REST API以服务于我们的移动应用,并使用Play!来提供网站,这样做是否有意义?如果是的话,如何在Play!框架中实现REST API呢?
6个回答

112

按照要求,一个简单的类似REST的方法。它几乎与CodeMwnci解决方案的工作方式相同,但使用Accept标头进行内容协商。首先是路由文件:

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

在这里您没有指定任何内容类型。我认为,只有当您想要为特定资源创建“特殊” URI 时才需要这样做。比如声明一个路由到/users/feed/,始终返回 Atom/RSS。

应用程序控制器如下所示:

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);
}

正如您所看到的,我仅删除了getUserJSON方法并将getUser方法重命名。为了使不同的内容类型能够运行,您现在需要创建多个模板,每种类型一个模板。例如:

user.xml:

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

user.json:

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

user.html:

<html>...</html>
该方法始终为浏览器提供HTML视图,因为所有浏览器都在其接受标头中发送text/html内容类型。所有其他客户端(可能是一些基于JavaScript的AJAX请求)都可以定义自己所需的内容类型。使用jQuery的ajax()方法,您可以执行以下操作: ```

该方法始终为浏览器提供HTML视图,因为所有浏览器都在其接受标头中发送text/html内容类型。所有其他客户端(可能是一些基于JavaScript的AJAX请求)都可以定义自己所需的内容类型。使用jQuery的ajax()方法,您可以执行以下操作:

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

这将以JSON格式提供ID为1的用户详细信息。Play目前本地支持HTML、JSON和XML,但您可以通过遵循官方文档或使用内容协商模块轻松使用其他类型。

如果您正在使用Eclipse进行开发,我建议使用REST客户端插件,该插件可让您测试路由及其相应的内容类型。


2
感谢您发布这篇文章。Play!文档是我见过的最好的解释基本结构的文档之一,但有时缺乏详细的示例。在同一个示例中演示两种方法确实能够澄清问题。 - Brad Mace
我正在尝试您的示例,我很好奇发布的JSON数据在哪里转换为User类。例如,在createUser函数内部,我发现newUser为空。 - Gary
2
@Gary:也许你使用了“user”而不是“newUser”?控制器和表单参数的名称必须匹配。我创建了一个简单的项目,展示了上述方法,包括所有用户的HTML/XML/JSON输出,网址为https://github.com/sebhoss/play-user-sample。 - seb
谢谢,我通过使用curl发送JSON字符串进行了测试,似乎play框架没有识别application/json内容类型:http://groups.google.com/group/play-framework/browse_thread/thread/ecabe33c5cd81e04/487fd2ad69535215?lnk=gst&q=json#487fd2ad69535215 - Gary
@Gary:感谢你的提示!看起来这个问题已经在主分支中修复了,你可以尝试自己构建并再次测试。 - seb
在Play 2.0中,这个解决方案还有效吗? - IcedDante

68

这仍然是一个热门问题,但得票最高的答案与当前版本的play不符。以下是一个在play 2.2.1下有效的REST示例:

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://dev59.com/AnE95IYBdhLWcg3wXcvd#2342589
}

我也想看到seb答案的更新版本,但不幸的是,你的回答删除了所有的.xml和.html魔法。 :-( - flaschenpost

26
使用Play!完成所有操作。在Play中编写REST服务非常容易。
首先,路由文件使编写符合REST方法的路由变得简单明了。
然后,在控制器中为每个要创建的API方法编写操作。
根据您想要返回结果的方式(XML、JSON等),有一些可以使用的方法。例如,使用renderJSON方法可以轻松地呈现结果。如果您想呈现XML,则可以像构建HTML文档视图一样进行呈现。
这是一个不错的例子。
路由文件
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

应用程序文件

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文件

<user>
   <name>${user.name}</name>
   <dob>${user.dob}</dob>
   .... etc etc
</user>

29
我认为在URI中明确指定表现形式并不更符合REST。更好的方法是直接使用Accept头,不要更改URI,因为您想要查看的资源保持不变。 上面的示例可以重写为只有一个getUser(Long id)方法,它与当前实现完全相同,但不是定义getUserJSON、getUserXML等,而是定义getUser.json和getUser.xml模板。尽管我会将其重命名为user.json/user.xml。 - seb
1
@seb - 你能把你的评论扩展成一个答案吗?我很想看看你所描述的技术的例子。 - Brad Mace
@Codemwnc,你能帮我看一下这个问题吗?http://stackoverflow.com/q/30190052/1584121 - SKK
你好,请帮我解决这个问题,我在这个链接中遇到了麻烦:https://dev59.com/XY_ea4cB1Zd3GeqPM1VO - Marco Dinatsoli
这项服务能被Java Swing客户端所使用吗? - nish1013
显示剩余3条评论

5
与JAX-RS实现集成是使用Play内置HTTP路由的可能替代方法。有关RESTEasy示例,请参见RESTEasy Play!模块
如果您已经投资于JAX-RS,或者需要一些高级功能,比如内容协商,那么这种方法是有意义的。如果不需要这些功能,那么直接使用Play来响应HTTP请求并提供JSON或XML会更简单。

4

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接