2011-08-23 37 views
22

Estoy luchando con la forma correcta de implementar los servicios Spring MVC 3.x RESTful con HATEOAS. Tenga en cuenta las siguientes limitaciones:Spring MVC, REST y HATEOAS

  • No quiero que las entidades de mi dominio estén contaminadas con construcciones web/de reposo.
  • No quiero que mis controladores estén contaminados con construcciones de vista.
  • Quiero admitir vistas múltiples.

Actualmente tengo una aplicación MVC muy bien preparada sin HATEOAS. Las entidades de dominio son POJO puros sin conceptos de vista o web/resto incrustados. Por ejemplo:

class User { 
    public String getName() {...} 
    public String setName(String name) {...} 
    ... 
} 

Mis controladores también son simples. Proporcionan enrutamiento y estado, y delegan al marco de resolución de vista de Spring. Tenga en cuenta que mi aplicación es compatible con JSON, XML, HTML y, sin embargo, no hay entidades del dominio o los controladores se han incorporado información de vista:

@Controller 
@RequestMapping("/users") 
class UserController { 

    @RequestMapping 
    public ModelAndView getAllUsers() { 
    List<User> users = userRepository.findAll(); 
    return new ModelAndView("users/index", "users", users); 
    } 

    @RequestMapping("/{id}") 
    public ModelAndView getUser(@PathVariable Long id) { 
    User user = userRepository.findById(id); 
    return new ModelAndView("users/show", "user", user); 
    } 
} 

Así pues, ahora mi problema - no estoy seguro de una manera limpia para apoyar HATEOAS. Aquí hay un ejemplo. Digamos que cuando el cliente pide un usuario en formato JSON, que salga de esta manera:

{ 
    firstName: "John", 
    lastName: "Smith" 
} 

Supongamos también que cuando yo apoyo HATEOAS, quiero que el JSON para contener un simple enlace de "auto" que el cliente puede usar para actualizar el objeto, eliminarlo o cualquier otra cosa. También podría tener un enlace de "amigos" que indica cómo obtener la lista de usuarios de amigos:

{ 
    firstName: "John", 
    lastName: "Smith", 
    links: [ 
    { 
     rel: "self", 
     ref: "http://myserver/users/1" 
    }, 
    { 
     rel: "friends", 
     ref: "http://myserver/users/1/friends" 
    } 
    ] 
} 

De alguna manera me desea adjuntar enlaces a mi objeto. Siento que el lugar correcto para hacerlo es en la capa de controladores, ya que todos los controladores conocen las URL correctas. Además, dado que soporto múltiples vistas, siento que lo correcto es decorar de alguna manera las entidades de mi dominio en el controlador antes de que se conviertan a JSON/XML/lo que sea en el marco de resolución de vistas de Spring. Una forma de hacerlo podría ser envolver el POJO en cuestión con una clase genérica de Recursos que contenga una lista de enlaces. Sería necesario ajustar algunas vistas para ajustarlo al formato que quiero, pero es factible. Lamentablemente, los recursos anidados no se pudieron ajustar de esta manera. Otras cosas que me vienen a la mente incluyen agregar enlaces a ModelAndView, y luego personalizar cada uno de los resolvedores de vista listos para usar de Spring para agregar enlaces al JSON/XML/etc generado. Lo que no quiero es estar constantemente creando a mano JSON/XML/etc. para acomodar varios enlaces a medida que van y vienen durante el curso del desarrollo.

¿Pensamientos?

+2

Ahora hay un proyecto de Spring Data llamado [Spring Data - Rest] (http://www.springsource.org/spring-data/rest) que admite HATEOAS de alguna forma. – SingleShot

+1

o puede usar el módulo independiente [spring-hateoas] (https://github.com/SpringSource/spring-hateoas) directamente. – Mariusz

Respuesta

10

Hay un proyecto muy útil llamado Spring HATEOAS en GitHub que tiene la siguiente descripción:

"Este proyecto ofrece algunas API para facilitar la creación de RESTO representaciones que siguen el principio HATEOAS cuando se trabaja con la primavera y especialmente Primavera MVC"

Si la clase de recurso que se extiende a devolver 'ResourceSupport' puede agregar fácilmente enlaces a la misma, y ​​se puede construir utilizando enlaces 'ControllerLinkBuilder', por ejemplo, para añadir un enlace auto:

import static org.sfw.hateoas.mvc.ControllerLinkBuilder.*; 

Link link = linkTo(YourController.class).slash(resource.getName()).withSelfRel(); 
resource.add(link); 

Se trata de un nuevo proyecto bastante pero está disponible en el repositorio Maven pública, si es necesario:

<dependency> 
    <groupId>org.springframework.hateoas</groupId> 
    <artifactId>spring-hateoas</artifactId> 
    <version>0.3.0.RELEASE</version> 
</dependency> 

Si utiliza el artefacto experto:

org.sfw.hateoas.mvc.ControllerLinkBuilder 

se convierte en:

org.springframework.hateoas.mvc.ControllerLinkBuilder 
+0

Interesante. Prefiero que las importaciones de Spring y otros códigos de infraestructura estén en la periferia de mi aplicación, pero en este caso todas las clases de recursos que extienden 'ResourceSupport' parecen una compensación decente. ¡Gracias! – SingleShot

+2

Ah, y es de Oliver Gierke, quien es el chico principal de Spring Data. Guay. – SingleShot

+0

Escribí un blog relacionado con [HATEOAS usando Spring Framework] (http://azagorneanu.blogspot.com/2013/06/hateoas-using-spring-framework.html) –

1

Mis Pensamientos:

  • utilizando algún tipo de convención de nomenclatura de modo que, por ejemplo, la URL de referencia sí se puede construir fuera del nombre de la clase del objeto.
  • No creo que el controlador deba agregar elementos a los enlaces (por ejemplo, usted escribió "No quiero que mis controladores estén contaminados con construcciones de vista". Intentaría buscar una forma de extender la serialización JSON para que agregue automáticamente las cosas adicionales. Puede que necesite agregar algunas anotaciones a sus entidades, incluso si esto las contaminaría un poco.
+1

Gracias. La convención de nomenclatura podría funcionar si todo lo que quería era un enlace "propio", sin embargo, en la aplicación real habría múltiples enlaces específicos del flujo de trabajo en los objetos (similar al algo famoso ["cómo tomar una taza de café"]). http://www.infoq.com/articles/webber-rest-workflow) artículo) que no pudo ser creado por convención. – SingleShot

+0

En cuanto a la generación de enlaces, necesito encontrar algo genérico, así que cuando agrego un nuevo enlace no tengo que ir a editar mis 3 resoluciones de vista diferentes (JSON/XML/HTML). Hmmm.Tal vez la naturaleza de una arquitectura RESTful con esta tecnología requiere que las entidades de dominio estén "contaminadas" con enlaces, y tal vez eso no esté mal ... – SingleShot

+2

La "convención de nomenclatura" es una mala idea. Específicamente, parece totalmente anti-HATEOAS. El objetivo de HATEOAS es que el cliente no necesita "conocer" su API, sino que simplemente puede seguir los enlaces para llegar a otra entidad o transición de estado. – HDave

1

Acabo de tropezar con esto mientras buscaba algo más y pensé que debería considerar usar el encabezado Link en lugar de contenido en el cuerpo JSON que realmente está contaminando las representaciones de sus recursos.

Compruebe el IETFs memo on web linking y también el IANA registry of link relations.

0

PARA crear el enlace en su api REST puede usar el proyecto HAETOAS de Spring framework.

org.springframework.hateoas.mvc.ControllerLinkBuilder clase han puesto de método que se puede utilizar para construir un enlace como -

Link link=linkTo(PersonControllerImpl.class).slash(null).withSelfRel(); 

También si usted tiene un método controlador que tiene @RequestMapping anotación con algún valor URI -

@RequestMapping(value = "/customer", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE}) 
    public ResponseEntity<?> getCustomer() { 

//... 
} 

entonces puede crear un vínculo usando el valor URI del método como -

linkTo(methodOn(PersonControllerImpl.class).getCustomer()).toUri().toString() 

devolverá el valor String (http://www.urhost.com/customer) que puede configurar en su entityObject.