如何在JAX-RS中管理状态?

时间:2016-04-18 17:04:21

标签: java rest jax-rs wildfly resteasy

如何配置JAX-RS 2实现(RESTEasy 3)以将应用程序的状态发送到客户端?

在JSF中,我可以使用STATE_SAVING_METHOD参数。

是否有使用JAX-RS的标准方法?

<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
</context-param>

只是举例说明我的问题,我想配置JAX-RS提供程序将cart变量状态返回给客户端,这样我就不会将数据保留在内存中或持久化会话状态。

@Path("/cart")
public class ShoppingCart {

    // List of products presented to the user
    private List<Product> availableProducts;

    // Products selected by the user
    private List<Product> cart;

    @GET
    public List<Product> addProduct() {
        return availableProducts;
    }

    @POST
    public void addProduct(Product product) {
        cart.add(product);
    }

}

更新

我想添加一些支持管理应用程序状态的引用。我同意在理论上,所有服务都应该是无状态的,但实际上我发现,如果可以维护状态,特别是出于安全和生产力的原因,许多情况会更有意义。

模式似乎通常通过超链接完成,内容经过适当加密以保证数据和完整性。我现在正在研究支持这些场景的JAX-RS工具。

来自 RESTful Web服务手册

How to Manage Application State

  

由于HTTP是无状态协议,因此每个请求都独立于任何请求   先前的请求。但是,交互式应用通常需要   客户遵循特定顺序的一系列步骤。这个   强制服务器临时存储每个客户端的当前位置   协议之外的那些序列。诀窍是管理国家   这样你就可以在网络之间取得平衡   性能和可扩展性。

还来自JAX-RS documentation

  

通过超链接进行有状态的互动:每次与a的互动   资源是无国籍的;也就是说,请求消息是自包含的。   有状态的交互基于显式状态的概念   传递。存在几种交换状态的技术,例如URI   重写,cookie和隐藏的表单字段。国家可以嵌入   响应消息以指向交互的有效未来状态。   请参阅使用实体提供程序映射HTTP响应和请求实体   有关更多信息,请参阅JAX-RS概述文档中的实体和“构建URI”   信息。

2 个答案:

答案 0 :(得分:3)

虽然lefloh提供了非常清晰的answer,但我想添加一些有助于您了解REST架构的详细信息。

REST架构风格

REST 代表重新表示 S tate T 转移。此体系结构协议无关,但经常通过HTTP协议实现。

REST架构风格在Roy Thomas Fielding博士论文的chapter 5中定义。并且在这种架构风格中添加了以下约束:

通过应用上面定义的约束,可以引入某些架构属性,例如可见性,可移植性,可靠性,可伸缩性和网络效率。

无状态约束

在REST应用程序中,从客户端到服务器的每个请求都必须包含服务器要理解的所有必要信息。有了它,您不依赖于服务器上存储的任何会话上下文,也不会破坏stateless constraint

  

5.1.3 Stateless

     

[...]通信本质上必须是无状态的[...],这样每个从客户端到服务器的请求必须包含理解请求所需的所有信息,并且不能利用任何存储的上下文。服务器。因此,会话状态完全保留在客户端上。 [...]

例如,在访问需要身份验证的受保护资源时,每个请求都必须包含要经过适当身份验证/授权的所有必需数据。这意味着将对每个请求执行身份验证

JAX-RS生命周期

使用JAX-RS创建REST应用程序时,您有资源类,即使用JAX-RS注释实现相应Web资源的Java类。

资源类是至少有一个使用@Path或请求方法指示符(@DELETE@GET@HEAD@OPTIONS注释的方法的POJO, @POST@PUT,使用@HttpMethod创建的自定义注释。

根据JAX-RS specification,资源类遵循明确定义的生命周期:

  

3.1.1生命周期与环境

     

默认情况下为每个对该资源的请求创建一个新的资源类实例。首先调用构造函数,然后注入任何请求的依赖项,然后调用适当的方法,最后使对象可用于垃圾收集。 [...]

在JAX-RS中,没有这样的javax.faces.STATE_SAVING_METHOD或类似的。没有国家。没有会话。

您的资源类可以遵循leflohanswer中建议的伪代码。不要忘记,您始终可以在资源类中执行注入:

@Path("/cart")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class ShoppingCart {

    @Inject
    private ShoppingCartService service;

    @GET
    public Response getProducts() {
        ...
    }

    @POST
    public Response addProduct(Product product) {
        ...
    }
}

JAX-RS中的超链接

REST的一个非常重要的方面是超链接,URI,用于客户端可用于将Web服务转换为新应用程序状态的表示形式。这被称为超媒体作为应用程序状态的引擎,经常被视为 HATEOAS

JAX-RS 2.0引入了Link类,它用作RFC 5988中定义的Web链接的表示。 JAX-RS Link类添加了API支持,以便在HTTP消息中提供其他元数据。

可以将Link序列化为HTTP响应作为额外的HTTP标头(可能提供了多个Link标头,因此可以在单个消息中提供多个链接)。这样的HTTP标头可能如下所示:

Link: <http://example.com/api/books/1/chapter/2>; rel="prev"; title="previous chapter"

使用JAX-RS API生成Link可以完成如下:

Response response = Response.ok()
                      .link("http://example.com/api/books/1/chapter/2", "prev")
                      .build();

Link的实例也可以通过调用Link API上的一个工厂方法直接创建,该方法返回Link.Builder,可用于配置和生成新链接:< / p>

URI uri = URI.create("http://example.com/api/books/1/chapter/2");
Link link = Link.fromUri(uri).rel("prev").title("previous chapter").build();

Response response = Response.ok().link(link).build();

Link也可以添加到您的响应实体模型中:

public class Book {

    private String title;

    private Link link;

    // Default constructor, getters and setters ommited
}

例如,当序列化为JSON时,您将拥有类似的内容:

{
    "title" : "The Lord of the Rings",
    "link" : {
        "rel" : "self",
        "uri" : "http://example.com/api/books/1",
        "title": "self"
      }
}

答案 1 :(得分:2)

由于您通常在RESTful应用程序中避免使用服务器端状态,因此您无法将状态发送到客户端。你没有。通常,您通过URI标识资源并从例如数据库中获取它们。

在伪代码中,您可以这样实现:

@Path("{customerId}/cart")
public class ShoppingCart {

    @GET
    public List<Product> getProducts(@PathParam("customerId") long customerId) {
        // check if the caller has access-rights
        // fetch all products for given customerID from a database, 
        // and return it to the client
    }

    @POST
    public Response addProduct(@PathParam("customerId") long customerId, Product product) {
        // check if the caller has access-rights,
        // save the product in the database
        // and return a 201 maybe with the product as entity
    }

}