RESTful в игре! фреймворк

117

Мы планируем проект, в основном обслуживающий контент для мобильных приложений, но нам нужен веб-сайт.

У меня вопрос: есть ли смысл использовать Jersey или Restlet для разработки REST API для наших мобильных приложений, а затем использовать Play! для обслуживания веб-сайта.

Или имеет смысл просто использовать Play! делать все это? Если да, то как сделать REST с помощью Play! фреймворк?

Gary
источник

Ответы:

112

По запросу, простой подход, подобный REST. Он работает почти так же, как и решение Codemwncis, но использует заголовок 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 в своем заголовке Accept. Все другие клиенты (возможно, некоторые запросы AJAX на основе JavaScript) могут определять свой собственный желаемый тип контента. Используя метод jQuerys ajax (), вы можете сделать следующее:

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

Это должно дать вам подробную информацию о пользователе с идентификатором 1 в формате JSON. Play в настоящее время поддерживает HTML, JSON и XML изначально, но вы можете легко использовать другой тип, следуя официальной документации или используя модуль согласования содержимого .

Если вы используете Eclipse для разработки, я предлагаю использовать клиентский плагин REST, который позволяет вам тестировать ваши маршруты и соответствующий им тип контента.

СЕБ
источник
2
Спасибо, что разместили это. Игра! документы - одни из лучших, которые я видел для объяснения базовой структуры вещей, но иногда в них отсутствуют подробные примеры. Два подхода, продемонстрированные на одном примере, действительно проясняют ситуацию.
Брэд Мейс,
Я пробую ваш пример, мне любопытно, где опубликованные данные JSON преобразуются в класс User. например, внутри функции createUser я обнаружил, что newUser имеет значение null.
Гэри
2
@Gary: Может быть, вы использовали «user» вместо «newUser»? Имя контроллера и параметр формы должны совпадать. Я создал простой проект, который демонстрирует вышеуказанный метод, включая вывод HTML / XML / JSON для всех пользователей, на github.com/sebhoss/play-user-sample
seb
Спасибо, я протестировал его, используя curl для отправки строки JSON, и оказалось, что игровая платформа не распознала тип содержимого application / json: groups.google.com/group/play-framework/browse_thread/thread/…
Гэри,
@Gary: Спасибо за подсказку! Кажется, это исправлено в главной ветке, вы можете попробовать
собрать
68

Это все еще популярный вопрос, но ответы, получившие наибольшее количество голосов, не соответствуют текущей версии игры. Вот рабочий пример REST с игрой 2.2.1:

Conf / маршруты:

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)

приложение / контроллеры / 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(); // http://stackoverflow.com/a/2342589/1415732
}
Олден
источник
Я также хотел бы увидеть обновленную версию ответа seb, но, к сожалению, ваш ответ удалил всю магию .xml и .html. :-(
flaschenpost 05
26

Воспользуйтесь игрой! делать все это. Написание REST-сервисов в Play очень просто.

Во-первых, файл маршрутов упрощает запись маршрутов, соответствующих подходу 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>
Codemwnci
источник
Можно ли выбрать правильный метод getUser на основе заголовка Accept?
Тимо Весткемпер,
он есть, но не совсем надежен. Если play знает, что заголовок является запросом JSON, он попытается отобразить файл getuser.json. Если заголовок является xml, он попытается использовать getuser.xml. Тем не менее, пользователю / User / {id} / type гораздо проще понять и больше нравится REST
Codemwnci,
29
Я не думаю, что явно указывать тип представления в URI больше похоже на REST. Лучше использовать заголовок Accept напрямую и не изменять URI, поскольку ресурс, который вы хотите видеть, остается прежним. Приведенный выше пример можно переписать, чтобы иметь только один метод getUser (Long id), который выполняет то же самое, что и его текущая реализация, но вместо определения getUserJSON, getUserXML и т. Д. Вы скорее определяете шаблон getUser.json и getUser.xml. Хотя я бы тоже переименовал его в user.json / user.xml
сентябрь
Спасибо, это очень помогло. Ценить это!
Гэри
1
@seb - вы можете развернуть свой комментарий в ответ? Я бы хотел увидеть пример описываемой вами техники
Брэд Мейс
5

Интеграция с реализацией JAX-RS - возможный альтернативный подход к использованию встроенной HTTP-маршрутизации Play. Пример RESTEasy см. В разделе RESTEasy Play! модуль .

Этот подход имеет смысл, если вы уже инвестировали в JAX-RS или если вам нужны некоторые из расширенных функций REST, которые предоставляет JAX-RS, например согласование содержимого. В противном случае было бы проще просто использовать Play напрямую для обслуживания JSON или XML в ответ на HTTP-запросы.

Питер Хилтон
источник
2

Кажется, что этот подход не работает в версии 1.2.3 Play. Если вы загрузите исходный код, созданный @seb и упомянутый ранее https://github.com/sebhoss/play-user-sample , создание нового объекта пользователя с помощью POST с объектом JSON больше невозможно.

У вас должны быть определенные методы для создания, выполняемые с использованием POST-сообщений json и xml. Обрисовано здесь: https://groups.google.com/forum/#!topic/play-framework/huwtC3YZDlU

tchristensen
источник