-
Notifications
You must be signed in to change notification settings - Fork 11
HTTP services
Business is coded in action classes extending either from SafeAction
, or UnsafeAction
, and using POJOs to define input/output parameters.
According to rfc7231 section 4.2.1:
... Request methods are considered "safe" if their defined semantics are essentially read-only; i.e., the client does not request, and does not expect, any state change on the origin server as a result of applying a safe method to a target resource. Likewise, reasonable use of a safe method is not expected to cause any harm, loss of property, or unusual burden on the origin server...
This base clase is used to implement safe business logic, that is, logic that has no side-effects expected by the user. Results of these actions are cacheable, and both GET
and POST
request methods are allowed.
Example:
public class HelloWorldAction extends SafeAction<String, String> {
@Override
public Cacheable<String> execute(String name) {
return Cacheable.forForever("Hello " + name + "!");
}
}
On the other hand, this class is used to implement unsafe business logic, that has side-effects expected by the user, like for example, a state change in a business model.
Results of these actions are not cacheable, and only the POST
request method is allowed.
Example:
public class CheckoutAction extends UnsafeAction<Void, Void> {
@Override
public void execute() throws Exception {
// get shopping cart from HttpSession
// start transaction
// update stock
// perform payment
// end transaction
}
}
An idempotent action is an action that can be executed many times with the same outcome.
Action idempotence is specified by the method HttpAction.isIdempotent()
.
Action idempotence determines the HTTP methods allowed in the request and is important in building a fault-tolerant API, since gives freedom to intermediary agents in the client-to-server communication, to retry such idempotent requests in case of a communication failure.
- By definition
SafeAction.isIdempotent()
always returnstrue
- By default
UnSafeAction.isIdempotent()
returnsfalse
, and this can be overridden in action implementation.
Services can accept/return binary (non-JSON) payloads. This is possible due to the binary attachments support of JSON SPI.
Services accepting binary payloads define inside their inputs, properties of type InputStream
or MetaDataInputStream
.
The HTTP request in this case has to be a multipart POST
with a part (file upload) per stream. Input JSON data in the request must have a unique (string) value per stream matching the field name of the corresponding part. Parts corresponding to form fields must be present in the request body before file parts.
Implementation note: Given that the number of file parts is known in advance (can be inferred from the input data and schema), the framework only needs to save to disk the first files (see
org.brutusin.rpc.upload.folder
environment variable), being able to pass the last part stream directly to the action (if the action returns a binary payload all file parts are serialized, since duplex communication is not allowed in HTTP, and response writing must be started after finishing request reading). This way, last file part is recommended to be the largest one.
Request creation is simplified by the usage of the provided [Javascript API](Javascript API).
Click here to see a live example a service supporting binary uploading.
Actions can return arbitrary (non-JSON) payloads to the client if their output class is an instance of StreamResult.
Click here to see a live example a service supporting binary downloading.
##HTTP methods
|**`SafeAction`** |**`UnSafeAction`**
------------------|-----------------------|---------------
Idempotent | GET*
,POST
,PUT
|POST
,PUT*
Non-idempotent| |POST*
(*) recommended method
Parameter | Description |
---|---|
jsonrpc |
JSON-RPC 2.0 request object |
Depending on the JSON-RPC response payload, the following status codes are used in the HTTP response:
HTTP response status code | Case |
---|---|
200/304 | if error is null or error.code equals -32001 (see Caching section for more details) |
400 | if error.code equals -32700 or -32602
|
403 | if error.code equals -32000 ) |
404 | if error.code equals -32601 ) |
405 | if error.code equals -32002 ) |
500 | any other error |
The following content types can be returned:
- For JSON payloads:
application/json
. - For non-JSON payloads:
StreamResult.getContentType()
is used.
HTTP caching allows to:
- Reduce incoming request by allowing client caches to serve cached responses during a specific period ot time.
- Provide cache validation mechanisms that allow avoiding re-transmission of unchanged data (conditional requests)
The framework automatically handles HTTP caching depending on these factors:
- Action being safe.
-
Cacheable.getCachingInfo()
value returned bySafeAction.execute()
- Conditional request header
If-None-Match
present.
- If an error occurred (except
-32000
) or executionCachingInfo
isnull
or action is unsafe, then the response is considered not cacheable and the following HTTP headers are returned:
Expires:Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control:max-age=0, no-cache, no-store
Pragma:no-cache
- Else, if the action is safe, then the response is considered cacheable and the following HTTP headers are returned:
Expires:Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control:max-age={$maxAge}, {public|private}, [{no-store}]
Etag: W/"{$etag}
with Cache-Control
values obtained from the execution CacheInfo
, and etag
computed as a MD5
hash of the payload (for JSON responses), or the last modification date (for binary responses). Additionally, if the request is conditional and etags match, then the response status code is set to 304 (NOT MODIFIED)
and no payload is returned.
Note on POST
requests: When a POST request is received, all responses allowing caching additionally contain a Content-Location
header pointing to the url of the GET version, as explained in (rfc7231 4.3.3):
... POST caching is not widely implemented. For cases where an origin server wishes the client to be able to cache the result of a POST in a way that can be reused by a later GET, the origin server MAY send a 200 (OK) response containing the result and a Content-Location header field that has the same value as the POST's effective request URI...
Note on Expires
header: An Expires
header with an outdated value Thu, 01 Jan 1970 00:00:00 GMT
is returned in every response regardless of the case. This action is performed in order to avoid HTTP 1.0 caches contradicting the directives of the Cache-Control
header, at the cost of disabling caching for them.
An origin server might wish to use a relatively new HTTP cache control feature, such as the "private" directive, on a network including older caches that do not understand that feature. The origin server will need to combine the new feature with an Expires field whose value is less than or equal to the Date value. This will prevent older caches from improperly caching the response.
See rfc2616 sec14.9.3 for more details.
- Home
- Getting started
- Services
- HTTP services
- Messaging topics
- Spring
- Documenting
- Referencing source code
- Builtin components
- Configuration
- Deployment
- Client APIs
- Security
- Developer notes
- Architecture
- Examples