How to manage state in JAX-RS?

2019-07-17 12:31发布

How do I configure JAX-RS 2 implementation (RESTEasy 3) to send the state of the application to the client?

In JSF I am able to do it using the STATE_SAVING_METHOD parameter.

Is there a standard way of doing it using JAX-RS?

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

Just an example to illustrate my problem, I would like to configure the JAX-RS provider to return the cart variable state to the client, in that way I won't keep the data in-memory or persist the session state.

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

}

Update

I would like to add references that support the idea of managing the state of the appliations. I agree that in theory, all services should be stateless, but in practice I found out that many scenarios would make more sense if the state could be maintained, specially for security and productivity reasons.

It seems that the pattern is usually done via hyperlinks, with the content properly encrypted to secure data and integrity. I am now looking into the JAX-RS tools that support these scenarios.

How to Manage Application State from the RESTful Web Services Cookbook:

Since HTTP is a stateless protocol, each request is independent of any previous request. However, interactive applications often require clients to follow a sequence of steps in a particular order. This forces servers to temporarily store each client’s current position in those sequences outside the protocol. The trick is to manage state such that you strike a balance between reliability, network performance, and scalability.

And also from the JAX-RS documentation:

Stateful interactions through hyperlinks: Every interaction with a resource is stateless; that is, request messages are self-contained. Stateful interactions are based on the concept of explicit state transfer. Several techniques exist to exchange state, such as URI rewriting, cookies, and hidden form fields. State can be embedded in response messages to point to valid future states of the interaction. See Using Entity Providers to Map HTTP Response and Request Entity Bodies and “Building URIs” in the JAX-RS Overview document for more information.

2条回答
手持菜刀,她持情操
2楼-- · 2019-07-17 12:37

As you usually avoid serverside state in a RESTful application you simply can't send the state to the client. You don't have one. Usually you identify your resources by an URI and fetch them from for instance a database.

In pseudo-code you may implement it this way:

@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
    }

}
查看更多
家丑人穷心不美
3楼-- · 2019-07-17 12:53

While lefloh provided a very clear answer, I'd like to add a few more details that will help you to understand the REST architecture.

The REST architectural style

REST stands for Representational State Transfer. This architecture is protocol independent but it is frequently implemented over the HTTP protocol.

The REST architectural style was defined in the chapter 5 of Roy Thomas Fielding's PhD dissertation. And the following set of constraints was added to this architectural style:

Through the application of the constraints defined above, certain architectural properties are induced, such as visibility, portability, reliability, scalability and network efficiency.

The stateless constraint

In a REST applications, each request from the client to the server must contain all the necessary information to be understood by the server. With it, you are not depending on any session context stored on the server and you do not break the stateless constraint:

5.1.3 Stateless

[...] communication must be stateless in nature [...], such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. [...]

When accessing protected resources that require authentication, for example, each request must contain all necessary data to be properly authenticated/authorized. It means the authentication will be performed for each request.

JAX-RS lifecycle

When creating a REST application using JAX-RS, you have resource classes, that is, Java classes that uses JAX-RS annotations to implement a corresponding Web resource.

Resource classes are POJOs that have at least one method annotated with @Path or a request method designator (@DELETE, @GET, @HEAD, @OPTIONS, @POST, @PUT, custom annotations created with @HttpMethod).

According to the JAX-RS specification, resource classes follow a well defined lifecycle:

3.1.1 Lifecycle and Environment

By default a new resource class instance is created for each request to that resource. First the constructor is called, then any requested dependencies are injected , then the appropriate method is invoked and finally the object is made available for garbage collection. [...]

In JAX-RS, there's no such javax.faces.STATE_SAVING_METHOD or similar. There's no state. There's no session.

Your resource class can follow the pseudo-code suggested in lefloh's answer. And don't forget you always can perform injections into your resource class:

@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) {
        ...
    }
}

Hyperlinks in JAX-RS

A very important aspect of REST is hyperlinks, URIs, in representations that clients can use to transition the web service to new application states. This is known as hypermedia as the engine of application state, frequently abreviated as HATEOAS.

JAX-RS 2.0 introduced the Link class, which serves as a representation of the web link defined in RFC 5988. The JAX-RS Link class adds API support for providing additional metadata in HTTP messages.

A Link can be serialized to the HTTP response as additional HTTP header (there might be multiple Link headers provided, thus multiple links can be served in a single message). Such HTTP header may look like:

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

Producing Links with JAX-RS API can be done as following:

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

Instances of Link can be also created directly by invoking one of the factory methods on the Link API that returns a Link.Builder that can be used to configure and produce new links:

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

Links can be also added to your response entity models:

public class Book {

    private String title;

    private Link link;

    // Default constructor, getters and setters ommited
}

When serialized to JSON, for example, you will have something like:

{
    "title" : "The Lord of the Rings",
    "link" : {
        "rel" : "self",
        "uri" : "http://example.com/api/books/1",
        "title": "self"
      }
}
查看更多
登录 后发表回答