RESTful no Play! estrutura


117

Estamos planejando um projeto basicamente servindo conteúdo para aplicativos móveis, mas precisamos ter um site.

Minha dúvida é se faz sentido usar Jersey ou Restlet para desenvolver APIs REST para nossos aplicativos móveis e, em seguida, usar o Play! para servir o site.

Ou faz mais sentido apenas usar o Play! fazer tudo? Em caso afirmativo, como fazer REST com o Play! estrutura?

Respostas:


112

Conforme solicitação, uma abordagem simples semelhante a REST. Funciona quase da mesma forma que a solução da Codemwncis, mas usa o cabeçalho Aceitar para negociação de conteúdo. Primeiro, o arquivo de rotas:

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

Você não especifica nenhum tipo de conteúdo aqui. Fazer isso é IMHO apenas necessário quando você deseja ter URIs "especiais" para determinados recursos. Como declarar uma rota para /users/feed/sempre retornar em Atom / RSS.

O controlador de aplicativo tem a seguinte aparência:

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

Como você pode ver, apenas removi o método getUserJSON e renomeei o método getUser. Para que diferentes tipos de conteúdo funcionem, agora você precisa criar vários modelos. Um para cada tipo de conteúdo desejado. Por exemplo:

user.xml:

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

user.json:

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

user.html:

<html>...</html>

Essa abordagem dá aos navegadores sempre a visualização HTML, já que todos os navegadores enviam um tipo de conteúdo text / html em seu cabeçalho Aceitar. Todos os outros clientes (possivelmente algumas solicitações AJAX baseadas em JavaScript) podem definir seu próprio tipo de conteúdo desejado. Usando o método jQuerys ajax (), você pode fazer o seguinte:

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

Isso deve fornecer a você os detalhes sobre o usuário com o ID 1 no formato JSON. Atualmente, o Play oferece suporte nativo a HTML, JSON e XML, mas você pode usar facilmente um tipo diferente seguindo a documentação oficial ou usando o módulo de negociação de conteúdo .

Se você estiver usando o Eclipse para desenvolvimento, sugiro usar o plugin do cliente REST, que permite testar suas rotas e o tipo de conteúdo correspondente.


2
Obrigado por postar isso. O jogo! documentos são alguns dos melhores que já vi para explicar a estrutura básica das coisas, mas às vezes faltam exemplos detalhados. Ter as duas abordagens demonstradas no mesmo exemplo realmente esclarece as coisas.
— Brad Mace

Estou testando seu exemplo, estou curioso para saber onde os dados JSON postados são convertidos para a classe de usuário. por exemplo, dentro da função createUser, descubro que newUser é nulo.
— Gary

2
@ Gary: Talvez você tenha usado "user" em vez de "newUser"? O nome do controlador e o parâmetro do formulário devem corresponder. Eu criei um projeto simples que mostra o método acima, incluindo HTML / XML / saída JSON para todos os usuários em github.com/sebhoss/play-user-sample
— seb

Obrigado, eu testei usando curl para enviar string JSON e parece que o framework do play não reconheceu o tipo de conteúdo application / json: groups.google.com/group/play-framework/browse_thread/thread/…
— Gary

@Gary: Obrigado pela dica! Parece que está corrigido no branch master, você pode tentar construí-lo você mesmo e depois testar novamente ..
— set

68

Esta ainda é uma pergunta popular, mas as respostas com maior votação não estão atualizadas com a versão atual do jogo. Aqui está um exemplo funcional de REST com 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(); // http://stackoverflow.com/a/2342589/1415732
}

Também gostaria de ver uma versão atualizada da Resposta de seb, mas infelizmente sua resposta removeu toda a magia .xml e .html. :-(
— flaschenpost

26

Use o Play! para fazer tudo. Escrever serviços REST no Play é muito fácil.

Em primeiro lugar, o arquivo de rotas torna simples escrever rotas que estejam em conformidade com a abordagem REST.

Em seguida, você escreve suas ações, no controlador, para cada método de API que deseja criar.

Dependendo de como você deseja retornar o resultado (XML, JSON etc), existem alguns métodos que você pode usar. Por exemplo, o uso do método renderJSON permite que os resultados sejam renderizados com muita facilidade. Se você deseja renderizar XML, pode fazê-lo da mesma maneira como faria para construir um documento HTML em sua Visualização.

Aqui está um bom exemplo.

arquivo de rotas

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

Arquivo de aplicativo

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

arquivo getUser.xml

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

É possível escolher o método getUser correto com base no cabeçalho Aceitar?
— Timo Westkämper

é, mas não totalmente confiável. Se o play souber que o cabeçalho é uma solicitação JSON, ele tentará renderizar um arquivo getuser.json. Se o cabeçalho for um xml, ele tentará getuser.xml. No entanto, é muito mais fácil de entender, e mais semelhante ao REST, para user / User / {id} / type
— Codemwnci

29
Não acho que seja mais semelhante a REST especificar o tipo de representação explicitamente no URI. É melhor usar o cabeçalho Aceitar diretamente e não alterar o URI, pois o recurso que você deseja ver permanece o mesmo. O exemplo acima poderia ser reescrito para ter apenas um único método getUser (Long id) que faz exatamente o mesmo que sua implementação atual, mas em vez de definir um getUserJSON, getUserXML, etc., você deve definir um modelo getUser.json e getUser.xml. Embora eu mudar o nome que a user.json / user.xml também
— seb

Obrigado, isso é muito útil. Agradeço!
— Gary

1
@seb - você pode expandir seu comentário para uma resposta? Adoraria ver um exemplo da técnica que você descreve
— Brad Mace

5

A integração com uma implementação JAX-RS é uma possível abordagem alternativa ao uso do roteamento HTTP integrado do Play. Para um exemplo do RESTEasy, consulte o RESTEasy Play! módulo .

Essa abordagem faz sentido se você já investiu em JAX-RS ou se precisa de alguns dos recursos avançados REST que o JAX-RS fornece, como negociação de conteúdo. Caso contrário, seria mais simples usar o Play diretamente para servir JSON ou XML em resposta a solicitações HTTP.



Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.