Qore RestHandler Module Reference
1.5
|
The RestHandler module provides functionality for implementing REST services with the Qore HTTP server.
To use this module, use "%requires RestHandler"
and "%requires HttpServer"
in your code.
All the public symbols in the module are defined in the RestHandler namespace.
The main classes are:
see example file "restserver.q"
in the examples directory for an example of using this module to implement REST services.
The RestHandler class uses any of the following modules if the modules are available when the RestHandler module is initialized:
URL form encoding is supported as well for message bodies according to RFC 2738 2.2 with "Content-Type: application/x-www-form-urlencoded"
.
For standard REST web service development, the json module will be required to support JSON serialization.
The RestHandler class will automatically deserialize request bodies if the incoming MIME type is supported and additionally will serialize outgoing messages automatically based on the client's declared capabilities (normally responses are serialized with the same serialization as the incoming message).
The class that models a REST entity or object in this module is AbstractRestClass.
This class should be subclassed for each object type, and methods implemented for each REST method that the object will support.
Incoming requests matched by your RestHandler subclass (when called by the HttpServer when requests are appropriately matched) will be dispatched to methods in your AbstractRestClass specialization (ie your user-defined subclass of this class) first according to the following naming convention:
httpmethod
[RequestRestAction]For example:
"GET /obj HTTP/1.1"
: matches method AbstractRestClass::get()
"POST /obj HTTP/1.1"
: matches method AbstractRestClass::post()
"DELETE /obj/subobj"
: match method AbstractRestClass::del()
"GET /obj?action=status HTTP/1.1"
: matches method AbstractRestClass::getStatus()
"PUT /obj?action=status HTTP/1.1"
: matches method AbstractRestClass::putStatus()
"PATCH /obj?action=status HTTP/1.1"
: matches method AbstractRestClass::patchStatus()
In other words, if a REST action is given in the request (either as a URI parameter or in the message body), the first letter of the action name is capitalized and appended to a lower case version of the HTTP method name (except "DELETE"
is mapped to "del"
because "delete"
is a keyword); if such a method exists in your AbstractRestClass specialization, then it is called. If not, then, if the REST action exists under a different HTTP method (ie a request comes with "HTTP GET /something?action=setData"
, and "putSetData"
exists, but "getSetData"
was used for the search), then a 405 "Method Not Allowed"
response is returned. If no variation of the requested REST method exists, then a 501 "Not Implemented"
response is returned.
If a REST request is made with no action name, then a method in your class is attempted to be called with just the HTTP method name in lower case (except "DELETE"
is mapped to "del"
because "delete"
is a keyword).
HTTP Method Name to Qore Method Name
HTTP Method | Qore Method |
GET | AbstractRestClass::get() |
PUT | AbstractRestClass::put() |
PATCH | AbstractRestClass::patch() |
POST | AbstractRestClass::post() |
DELETE | AbstractRestClass::del() |
OPTIONS | AbstractRestClass::options() |
If no action is provided in the URL and the final URI path component does not match to a subclass, and an action method is defined in the last REST class, then this method will be run.
For example (assuming no subclass match for the trailing "status"
path component):
"GET /obj/status HTTP/1.1"
: matches method AbstractRestClass::getStatus()
"PUT /obj/status HTTP/1.1"
: matches method AbstractRestClass::putStatus()
"PATCH /obj/status HTTP/1.1"
: matches method AbstractRestClass::patchStatus()
It is recommended to avoid requiringg an "action"
parameter, because this approach is generally not compatible with REST schema solutions such as Swaggger/OAS or RAML.
In all cases, the signature for these methods looks like:
The arguments passed to the REST action methods are as follows:
"action"
argumnt was sent either in the message body or as a URI argument"action"
argument was sent)ah:
these are the parsed URI query arguments to the REST call; see Argument and Message Body Handling for more informationThe return value for these methods is the same as the return value for HttpServer::AbstractHttpRequestHandler::handleRequest().
consider the following example class:
Assuming this object corresponds to URL path /files/info.txt, the following requests are supported:
"GET /files/info.txt HTTP/1.1"
: calls the get() method above (no action)"PUT /files/info.txt/chown;user=username HTTP/1.1"
: calls the putChown() method above (such arguments could also be passed in the message body; see Argument and Message Body Handling for more information)"PUT /files/info.txt/chmod;mode=420 HTTP/1.1"
: calls the putChmod() method above (note that 420 = 0644)"PUT /files/info.txt/rename;newPath=/tmp/old.txt HTTP/1.1"
: calls the putRename() method above"PUT /files/info.txt?action=chown;user=username HTTP/1.1"
: calls the putChown() method above (such arguments could also be passed in the message body; see Argument and Message Body Handling for more information)"PUT /files/info.txt?action=chmod;mode=420 HTTP/1.1"
: calls the putChmod() method above (note that 420 = 0644)"PUT /files/info.txt?action=rename;newPath=/tmp/old.txt HTTP/1.1"
: calls the putRename() method aboveTo implement a hisearchy of REST objects in the URL, each AbstractRestClass subclass adds static subclass references using the AbstractRestClass::addClass() method and can also reimplement the subClassImpl() method to return a child AbstractRestClass object representing the child object programmatically based on REST API arguments or the current application state.
If no such sub object exists, then the method should return NOTHING, which will cause a 404 Bad Request
response to be returned by (to change this behavior, reimplement unknownSubClassError() in your specialization of RestHandler. Otherwise, if an exception occurs handling the request, a 409 Conflict
response is returned; see Exception Handling in REST Calls for more information.
The following is an example of a class implementing a subClassImpl() method:
There are two ways to pass arguments to REST action methods:
REST action methods have the following signature:
In the usual case when only one of the above methods is used, then the argument information is copied to both places, making it easier for the Qore REST implementation to handle arguments in a consistent way regardless of how they were passed. This means arguments (including any action
argument), can be passed in either the URI path as URI query arguments, or in the message body as a hash.
Therefore, the following cases are identical:
Request With URI Argument
PUT /files/info.txt/chmod;mode=420 HTTP/1.1 Accept: application/x-yaml User-Agent: Qore-RestClient/1.0 Accept-Encoding: deflate,gzip,bzip2 Connection: Keep-Alive Host: localhost:8001
Request With Arguments in the Message Body
PUT /files/info.txt/chmod HTTP/1.1 Accept: application/x-yaml User-Agent: Qore-RestClient/1.0 Accept-Encoding: deflate,gzip,bzip2 Connection: Keep-Alive Host: localhost:8001 Content-Type: application/json;charset=utf-8 Content-Length: 16 { "mode" : 420 }
GET
requests to be served with a message body, but this is not compliant with HTTP 1.1 RFCs and therefore could lead to compatibility problems should this technique be used with other servers; see HTTP GET Requests With a Message Body for more information.Qore exceptions are serialized in a consistent way by the RestHandler class so that they can be recognized and handled appropriately by clients.
Qore exceptions are returned with a 409 "Conflict"
HTTP return code with the message body as an ExceptionInfo hash.
RestHandler::getPossibleSubClasses()
to RestHandler::doGetPossibleSubClasses() in order to avoid a collision with a REST method name (issue 3614)"PUT path/xxx"
can be used instead of "PUT path?action=xxx"
. Additionally, support for fast static REST subclass lookups was added by moving the addClass() method from the RestHandler class to the AbstractRestClass class. (issue 2994)"PATCH"
methodOPTIONS
method