En la primera parte de este articulo vimos que aparecían, pero no los describimos, una serie de anotaciones que indican el método HTTP utilizado. Sin embargo no entramos en detalle en el porqué del uso de los mismo o como usar la anotación adecuada. Debido a que en esta parte tienen una participación más relevante, vamos a revisarlos un poco.
Verbos de HTTP
En HTTP se definen nueve métodos (a veces llamados verbos) que indican la acción a ser ejecutada sobre un recurso en particular. A continuación veremos los más usados.
El método GET es el pedido de una representación sobre un recurso. La información se entrega con un conjunto de encabezados y una representación. El cliente nunca envía una representación en un método GET. Se considera, junto a HEAD,como un método seguro, ya que no provoca cambio en el servidor.
El método HEAD es lo mismo que en GET, solo que la información que se envía como respuesta solo incluye los encabezados.
El método PUT es una afirmación sobre el estado de un recurso. El cliente usualmente envía una representación del recurso en un PUT y el servidor trata de cambiar o actualizar el recurso de forma tal que su estado se corresponda con el que tiene la representación. El método PUT, junto al DELETE, se definen como métodos idempotentes, es decir, hacer múltiples request a una URL deberían tener el mismo resultado que hacer uno solo. Los métodos seguros, también se consideran idempotentes.
El método DELETE una afirmación que dice que el recurso ya no debe existir. El cliente nunca envía una representación en el método DELETE.
El método POST es el envío de información para crear un nuevo recurso, modificar uno existente o las dos cosas a la vez. La representación enviada por método POST, describe el estado inicial del nuevo recurso. El método POST, no es necesariamente idempotente y por lo tanto, enviar múltiples POST puede tener (y en general es así) efectos secundarios no deseados.
POST vs PUT
La creación/actualización de recursos se puede hacer por medio del método POST, PUT o de ambos. La diferencia fundamental entre ambos métodos se ve reflejada en el significado de la URL. La URL en un método POST hace referencia al recurso que se encargara de administrar la entidad enviada. Ese recurso puede ser un proceso que acepte información, un Gateway hacia otro protocolo, etc. En cambio, la URL en un método PUT representa a la entidad en sí misma que debe ser actualizada o creada sin intermediarios.
Ahora que hemos repasado los diferentes métodos HTTP, vamos a seguir con nuestro ejemplo para la creación/utilización de servicios REST con CXF.
Pasaje de parámetros
La anotación @PathParam se utiliza para mapear las variables contenidas en el path a parámetros de un método, por ejemplo:
@Path("/customer/{id}")
public class CustomerService {
@PUT
@Path("{name}")
public Response updateCustomer(@PathParam("id") Long id, @PathParam("name") String name) {
...
}
}
En este caso, la variable id del la anotación de la clase, es mapeada a un parámetro de tipo Long, mientras que la variable name que aparece en el método, es mapeada a un parámetro de tipo String.
Tambien se soportan las anotaciones @QueryParam, @HttpHeader, @MatrixParam, @FormParam y @CookieParam y su eleccion dependera del metodo usado y de la ubicacion de los parametros. Los parametros pueden ser de tipo String o de cualquier tipo que posea un constructor estatico por medio del método valueOf(String s).
Sub-recursos
Un método de una clase/recurso que posea la anotación @Path y no posea una de método HTTP (como GET, POST, etc.) se considera como un sub-recurso. Los localizadores de sub-recursos se utilizan para resolver path hacia objetos que están mucho más adentro del árbol de servicios.
En nuestro ejemplo, el método getOrder, es un sub-recurso.
@Path("/customerservice/")
public class CustomerService {
@Path("/orders/{orderId}/")
public Order getOrder(@PathParam("orderId") String orderId) {
......
}
}
@XmlRootElement(name = "Order")
public class Order {
private long id;
private Items items;
public Order() {
}
public long getId() {
return id;
}
@GET
@Path("products/{productId}/")
public Product getProduct(@PathParam("productId")int productId) {
......
}
@Path("products/{productId}/items")
public Order getItems(@PathParam("productId") int productId) {
return this;
}
@GET
public Items getItems() {
......
}
}
Un pedido HTTP GET a http://localhost/customerservice/orders/223/products/323 produce que el método getOrder sea ejecutado. Si el recurso Order, cuyo id es 223, es encontrado, dicho recurso será utilizado para resolver el recurso Product. Eventualmente, un Product de id 223 que pertenezca a un Order de id 223 será devuelto.
Manejo de excepciones
Para devolver excepciones, uno puede o devolver una excepción no chequeada genérica o devolver un objeto javax.ws.rs.core.Response con un código de error. Esta segunda opción parece ser la más adecuada cuando se trabaja con JAX-RS.
Por ejemplo:
@Path("/customerservice/")
public class CustomerService {
@PUT
@Path("/customers/{id}")
public Response updateCustomer(@PathParam("id") Long id, Customer customer) {
return Response.status(errorCode).build();
}
@POST
@Path("/customers")
public Customer addCustomer(Customer customer) {
throw new WebApplicationException(errorCode);
}
}
Es posible cubrir casi todas las situaciones con los siguientes códigos de error:
Referencias:
http://en.wikipedia.org/wiki/Representational_state_transfer
http://cxf.apache.org/docs/jax-rs-basics.html
http://www.infoq.com/articles/rest-introduction
http://tomayko.com/writings/rest-to-my-wife
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6