GlassFish REST Resource Style and Behavior GuidelinesThe following document specifies the constraints that apply to all REST resources produced as part of the development effort, and acts as a set of guidelines for any external contributions. Adherence to these constraints will help ensure a consistent end-user experience.
Imperative Terms In SpecificationsThe key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document, and all other documents that comprise the specification of the GlassFish RESTful API, are to be interpreted as described in "Key words for use in RFCs to Indicate Requirement Levels" (RFC 2119). Request HeadersIn requests made to RESTful services, several specific HTTP headers are used as described in the following table:
Response HeadersIn responses returned by , several specific HTTP headers are used as described in the following table:
FIXME - Document cache control headers and behavior somewhere? Standard HTTP Status CodesREST resources will return standard HTTP response codes as described in the following table, under the conditions listed in the description.
HTTP Verb UsageTypically, RESTful services are implemented over HTTP, but this is not necessary to be considered RESTful. In the context of GlassFish RESTful services, however, all resources MUST be implemented over HTTP, and MUST use the HTTP verbs according to the following conventions:
Response Bodies for Successful RequestsSuccessful requests MUST return an HTTP status code of 200 (OK), 201 (Created), or 204 (No Content), to indicate that the requested action has been successfully performed, or 202 (Accepted) for long-running, asynchronous actions. In addition, response MAY include a response message body (with an appropriate media type) containing a representation of the requested information. The system supports three basic types of resources:
Each of these resources will return bodies tailored to the purpose of the resource. Collection ResourcesThe response of a collection resource will a list of the entities owned by the parent. For example, an /accounts/ resource would return a list of Account objects. To maintain some flexibility in the structure and content of the response body, this list (or JSON array) will be enclosed in a generic object. Such a response might look like this: > Link: http://localhost:4848/management/accounts/id/account1; rel="account"; title="account1" > Link: http://localhost:4848/management/accounts/id/account2; rel="account"; title="account2" > Link: http://localhost:4848/management/accounts/id/account3; rel="account"; title="account3" { items: [ {'name':'account1', description="Account 1"}, {'name':'account2', description="Account 2"}, {'name':'account3', description="Account 3"}, ] } In this example, the list returned is the full Account object. The objects in the list, though, do not specify how to address those objects directly. The client has two options: Using the name of the account, the appropriate URI can be calculated on the client and used in subsequent requests. This requires, though, that the client have a detailed knowledge of the server and how parent/child relationships are built and expressed. This results in a very tight coupling, making the client fragile, and server-side changes difficult, and/or expensive. Another option, though, is to use the metadata provided in the response headers. Link headers SHOULD be provided for each item in the list. Each header's rel attribute should specify the type of data represented by the link, and the title attribute should reflect the unique identifier used by the system for each entity. For example above we see three Link headers (each line prefixed with "> " as one might see returned from the curl command-line client) were returned, each one holding the URI for a given account. The rel attribute for each link was set to "account", as that is the type of the entity in question, and the title attribute was set to the account's name, as that is the primary key by which the system identifies a given account. Given this information, the client should easily be able to find the URI for a given Account in the items list. Entity ResourcesWhen a client accesses an entity resource, it expects the current state of the entity. There is, however, more to the entity than its state. For example, an entity might have child or action resources about which the client needs to be informed. As with collection resources, this type of information will be presented to the client via Link headers. There exists the possibility, though, that extra data, such as error messages, etc. may also need to be sent to the client, so the entity will be enclosed in an outer, generic object, under the key "item": > Link: http://localhost:4848/management/accounts/id/account1; rel="self" > Link: http://localhost:4848/management/accounts/id/account1/services; rel="services" { "item": { "name": "account1", "description": "Account 1" } } In this example, we see that an account can have services associated with it. The client, then, already aware of this through domain knowledge acquired during requirements gathering and system design, knows to look for a link with a rel value of "services" to find the URI to fetch this type of information. The client is, of course, free to compute the URI itself, but the fragility and coupling issues discussed above apply here as well. In this example, another Link header was provided, this one with the rel value of "self". Many REST users are accustomed to seeing this type of self-referential data in the response, so resources MAY return this if desired. Action ResourcesAn action resource, as noted in the Jersey documentation, is a concession to the real world fact that some things can't easily be encapsulated as a piece of state. The Jersey documentation uses the workflow of an online ordering system as an example: the client doesn't want or need to know the details. It just wants to pay for the order, ship it, etc. These types of interactions, then are exposed as "action resources", and they are children of the resource on which they act. In the example above, we may need to lock or unlock an account. Rather than exposing a "locked" property and requiring that the user discover the property then somehow learn how to set it, we can model this actions. These are discovered via another set of Link headers: > Link: http://localhost:4848/management/accounts/id/account1; rel="self" > Link: http://localhost:4848/management/accounts/id/account1/services; rel="services" > Link: http://localhost:4848/management/accounts/id/account1/lock; rel="action"; title="lock";op=POST > Link: http://localhost:4848/management/accounts/id/account1/unlock; rel="action"; title="lock";op=POST { "item": { "name": "account1", "description": "Account 1" } } This is the same entity as above, but we've added two more Link headers. The rel value of "action" identifies this as an action resource URL, the title says what it is, and op tells the client the HTTP method to use. The response from invoking such an action MUST include the appropriate status code (see table above) and MAY include a message object in the body of the response. Interaction ModesREST resources are typically consumed in a synchronous fashion: the client makes the request, then waits for the response. Modern REST resources, though, may offer more interesting means of client/server interaction including asynchronous calls and server-sent events. The GlassFish RESTful Administration framework provides support for the following "modes":
Synchronous requestsThis is the standard/normal means of interacting with a REST resource and need not be elaborated upon here. Asynchronous requestsTechnically, asynchronous requests (in JAX-RS terms) are a purely client-side concern. There is no on-the-wire difference for these, but, for the sake of completeness, an example is included here: public void asyncRestClient() throws JSONException, InterruptedException { getClient() .target(restUrl) .request(MediaType.APPLICATION_JSON) .async() // #1 .post(Entity.entity("Here is some text", MediaType.TEXT_PLAIN), new InvocationCallback<Response>() { // #2 @Override public void completed(Response response) { processResponse(response.readEntity(JSONObject.class)); } @Override public void failed(ClientException ce) { // Do something } }); } This simple example shows how to use the JAX-RS Client API to make an asynchronous request. First, a call to async() is added to the call chain (#1), and, second, an instance of InvocationCallback to is passed to post() (#2). The Client creates a background thread to handle the request. This thread sends the request, then blocks, waiting for the response. Once the response is received, it calls completed() on the InvocationCallback object. The REST client code can then read the entity off the Response, and pass it along to a business method for processing. If an error occurs, the Client will call failure(), at which point the client should handle the error in a manner appropriate for its context. Server-sent eventsGlassFish resources can, if written to do so, push ProgressStatus events to the client, allowing the client to display the status of a long-running request. The ProgressStatus object will look like this (formatted for readability): event: AdminCommandInstance/stateChanged data: { "state":"RUNNING", "id":"1", "empty-payload":true } GlassFish Currently provides a JAX-RS MessageBodyReader to read this entity off the request and return a ProgressStatus object which the client can consume. In a typical SSE-based requests, three different event types will be seen. AdminCommandInstance/stateChanged RUNNING: { "state":"RUNNING", "id":"1", "empty-payload":true } COMPLETED: { "state":"COMPLETED", "id":"1", "empty-payload":true, "action-report": { "message":"Slow command completed.", "command":"slow-command AdminCommand", "exit_code":"SUCCESS", "extraProperties": { "response":{ "messages":[{"message":"It works!","severity":"SUCCESS"}] } } } } ProgressStatus/state { "progress-status":{ "name":"slow-command", "id":"1", "total-step-count":-1, "current-step-count":0, "complete":false } } ProgressStatus/change { "progress-status-event":{ "changed":["SPINNER","STEPS"], "message":"Finished step #1", "progress-status":{ "name":"slow-command", "id":"1", "total-step-count":5, "current-step-count":1, "complete":false } } } The last event of this type should have its complete flag set to true. A client of an SSE-based resource is considerably more complex than a normal client, so an example is included here. Detached requestsThis interaction mode is a GlassFish-specific mode and, like server-sent events, may not supported by every resource. In a nutshell, requests of this type will return a URI to a job ID, rather than, say, an entity. To request a detached execution, the client should send the query parameter __detached with an arbitrary value (only the presence of the parameter is important). Resources which support this MUST return 201 (Accepted), and SHOULD return two headers: Location, which is the URI by which clients can check the status of the detached job, and, optionally, X-Location which gives the URI of the entity should the detached execution ultimately succeed. (NOTE: X-Location, both it's presence and its name, may change for the final release of the product). Client ModelsTo help with authoring client applications, as well as helping automate the serialization and deserialization of the entity state, each REST resource SHOULD expose on or more model Objects which hold the state of the resource. The system will then encode this resource as JSON (or any other supported encoding). If using such a model is not desired, the module providing the resource MUST provide a means to encode and decode the entity state. This means SHOULD integrate with REST interface architecture, which is described in another document. These models, if used, SHOULD adhere to the JavaBeans specification as much as possible to ensure consistent on-the-wire encoding (see next section). Encoded Client StructureThe client state, when encoded, should be done in as consistent a manner as possible. To that end, the following rules SHOULD be followed when encoding values:
"items":["item1", "item2", "itemN"] while maps will be designated using braces ({ and }), even for single item collections: "someObject" : {"property1": "string value", "property2" : 2 } Error Response Message BodiesIt is, of course, possible for a number of things to go wrong. The various underlying causes are described (as discussed above) by various HTTP status codes in the range 400-499 (for client side errors) or 500-599 (for server side problems). The description of each request type MAY list the possible status codes returned by that request type. If a response is returned with an error status code (400-499 or 500-599), the server SHOULD also return a message body containing an error object containing the message(s) which describe what went wrong. The values of such messages might be used, for example, to communicate with a human user of the client side application. Sensitive information, such as stack traces, SHOULD NOT be reflected to the user. For example: { "validationErrors" : [ { "field" : "emailAddress", "message" :"The value of emailAddress is not a valid email address" }, { "field" : "birthDate", "message" : "The value of birthDate can not be a future date" } ] } Design PatternsThis section will detail a list of design patterns and behaviors that SHOULD be implemented in all resources (where appropriate). Entity CreationAfter an entity is created on the server, the response to the client SHOULD include the URI to new entity via the Location header, as well as the entity itself in the body of the response. Validation FailureIf an entity is being updated and a validation error occurs, the resource SHOULD attempt to validate as much of the submitted entity as possible, then it SHOULD return a 400 (Bad Request) with the validation failures returned as an object with a property, validationErrors, whose value is a list of strings detailing the reasons for the failure: { "validationErrors" : [ {"field" : "field1" , "message" : "Reason 1"}, {"field" : "field2" , "message" : "Reason 2"}, {"field" : "field3" , "message" : "Reason n"} ] } Collection ResourcesWhen making a GET request against a collection resource, the collection SHOULD be returned as an object with a property, children, whose value is a list of the URIs of the child resources: { "children": [ {"uri" : "http://machine:port/uri/for/child1"}, {"uri" : "http://machine:port/uri/for/childN"} ] } In some scenarios, it may be desired to return the child entities with the request. In these situations, the resource can identify such a request from the client via the presence of the _full ( TODO: discuss this name ) query parameter. If present, the resource MAY honor the request. If the request will result in a significant use of system resources (memory, bandwidth, etc), the resource MAY ignore the request to return the child entity states. URI AddressabilityGenerated URIs SHOULD be addressable by the client; i.e., The machine, port, etc SHOULD reflect that specified by client, meaning that the presence of any NAT/firewall in the network SHOULD NOT result in the generation of unreachable URIs. Authentication and AuthorizationIf security is enabled (as it may not be in a development environment) and a request is made to a resource by an unauthenticated user, the resource MUST return 401 (Unauthorized) If security is enabled and a request is made to a resource to which the client does not have permissions, the system MUST return 403 (Forbidden). If security is enabled and a request is made to a resource to which the client has permission, but the entity contains data to which the client does not have permission, the resource MAY return only the portion to which the client has permission, or the resource MAY reject the entire request with a 403 (Forbidden) response. In the latter case, the system SHOULD include an explanatory error message in the body. Multiple DeleteDeleting multiple entities at one time is often desired for performance reasons. Resources that support this operation MUST maintain fidelity with the spirit of REST and implement the method using DELETE. The IDs of the entities to be delete SHOULD be specified by repeated id=value query parameters. Open Questions
|