ResourceControlleris a controller that provide conveniences for implementing endpoint controllers. A
ResourceControllermust be subclassed, and in that subclass, you write a method for each operation on that type of resource. For example, a
UserControllermight handle the following operations:
ResourceControllersubclass that has an
@Operationannotation. It must return an instance of
Future<Response>. Here's an example:
GETrequests without path variables. To handle operation methods with path variables, the name of the path variable is added to the
Operationtells us which HTTP method the operation method handles. The following named constructors exist:
Operation()constructor takes the HTTP method as its first argument for non-standard operations, e.g.:
Operationconstructors take a variable list of path variables. There can be multiple path variables for an operation. An operation method will only be invoked if all of its path variables are present in the request path. There can be multiple operation methods for a given HTTP method, as long as each expects a different set of path variables.
ResourceControllersubclass must be preceded by a
Routerin the application channel. The
Routerwill parse path variables so that the controller can use them to determine which operation method should be invoked. A typical route to a
ResourceControllercontains an optional identifying path variable:
CityControllerto implement operation methods for all HTTP methods with both no path variables and the 'name' path variable.
/cities/[:name/[attractions/[:id]]], while valid, makes controller logic much more unwieldy.
DateTime. Simply declare the bound parameter's type to the desired type:
Stringor any type that implements
DateTime). Query parameters may also be bound to
boolparameters; a boolean query parameter will be true if the query parameter has no value (e.g.
int cityID- if the path variable 'id' can't be parsed into an
int, a 404 Not Found response is sent. If a query parameter or header value cannot be parsed, a 400 Bad Request response is sent.
List<T>parameters to headers and query parameters, where
Tmust meet the same criteria as above. Query parameters and headers may appear more than once in a request. For example, the value of
[1, 2]if the request URL ends with
/path?id=1&id=2and the operation method looks like this:
X-API-Keyheader is present in the request, its value will be available in
apiKey. If it is not,
getAllCities(apiKey)would not be called and a 400 Bad Request response will be sent. If
apiKeywere optional, the method is called as normal and
apiKeyis null or a default value.
apiKeywill be bound in all cases.
Routermust have a route that includes a path variable and that path variable must be listed in the
Operationannotation. Path variables are case-sensitive and may not be optional.
Operation, you will get a runtime exception at startup. You do not have to bind path variables for an operation method to be invoked.
Bind.body()doesn't take any identifying arguments (however, it does take optional arguments for ignoring, requiring or rejecting keys; this matches the behavior of
Serializable.readand only works when the bound type is a
Serializableor list of).
Cityin this example) must implement
Serializable. Conduit will automatically decode the request body from it's content-type, create a new instance of the bound parameter type, and invoke its
readmethod. In the above example, a valid request body would be the following JSON:
readthrows an exception, a 400 Bad Request response will be sent and the operation method won't be called.
List<Serializable>parameters to the request body. Consider the following JSON that contains a list of cities:
Listof the desired type:
Content-Typeis 'x-www-form-urlencoded', its must be bound with
ResourceControllers may also have
Bind.headermetadata. This binds values from the request to the
ResourceControllerinstance itself, making them accessible from all operation methods.
limitare bound prior to
getCitiesbeing invoked. By default, a bound property is optional. Adding an
requiredBindingannotation changes a property to required. If required, any request without the required property fails with a 400 Bad Request status code and none of the operation methods are invoked.
ResourceControllers have some other behavior that is important to understand.
ResourceControllercan limit the content type of HTTP request bodies it accepts. By default, a
ResourceControllerwill accept only
application/jsonrequest bodies for its
PUTmethods. This can be modified by setting the
acceptedContentTypesproperty in the constructor.
ResourceControllerprior to your operation method being invoked. Therefore, you can always use the synchronous
RequestBody.asmethod to access the body from within an operation method:
ResourceControllercan also have a default content type for its responses. By default, this is
application/json. This default can be changed by changing
responseContentTypein the constructor:
responseContentTypeis the default response content type. An individual
Responsemay set its own
contentType, which takes precedence over the
responseContentType. For example, the following controller returns JSON by default, but if the request specifically asks for XML, that's what it will return:
Query<T>based on the incoming request. If the request has a body, this
valuesproperty is read from that body. If the request has a path variable, the
Query<T>assigns an expression to the primary key value. For example, in a normal
ResourceControllerthat responds to a PUT request, you might write the following:
QueryController<T>builds this query before a operation method is invoked, storing it in the inherited
ManagedObject<T>subclass is the type argument to
ManagedObjectController<T>is significantly more powerful; you don't even need to subclass it. It does all the things a CRUD endpoint does without any code. Here's an example usage:
GET /users- can be modified with query parameters. For example, the following request will return the users sorted by their name in ascending order:
ManagedObjectController<T>can also be subclassed. A subclass allows for callbacks to be overridden to adjust the query before execution, or the results before sending the respond. Each operation - fetch, update, delete, etc. - has a pair of methods to do this. For example, the following subclass alters the query and results before any update via