You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The current approach doesn't allow specifying a base path. To allow this, the class level @webservice annotation will allow an optional "path" key. If it supplies a value, it will be prepended to all "path" keys defined in methods.
To allow accessing headers, the "inject" key will be changed to four annotations: @param for query parameters, @path for path parameters, @header for headers, @cookie for cookies. These annotations will be method-level annotations but written in a way to become forward-compatible with the parameter annotations described in RFC Parameter annotations #218.
Parameters without annotation will be fetched from the payload. The special-case handling for the string "payload" will be removed.
To allow modifying the response other than its status code - to 200 (OK) and 500 (when an exception is raised) - methods may also return a Response instance.
The annotations are defined as follows:
@$<param>:<annotation> [ <name> ]
@$p: param // Use the request parameter "p"@$p: param('id') // Use the request parameter "id"
When deserializing parameters from strings, the parameter type is taken into account. If the parameter type is a value object, the reflected class is checked for either a public one-arg constructor or a public static valueOf() method.
To map exceptions, we will reuse the Response object as seen above. Exception mappers can be registered by binding them to the context, which can be retrieved via the well-known @inject annotation.
Class # Message
----------------------------------- --- --------------------------
lang.IllegalAccessException 403 Forbidden
lang.IllegalArgumentException 400 Bad request
lang.IllegalStateException 409 Conflict
lang.ElementNotFoundException 404 Not found
lang.MethodNotImplementedException 501 Method not implemented
lang.FormatException 422 Unprocessable entity
* 500 Internal Server Error
Mime type support
The default to map the mime type supplied with the client's Content-Type request header as follows: The pattern */json will map to the JSON serialization and */xml to the XML serialization. This means, custom mime types such as application/vnd.example.v3+json will work out of the box and will be mapped to the JSON serialization mechanism.
The Accept header will be parsed and returned media type will be determined by following the client-set preferences: The list is sorted by preference and the value with the highest preference then chosen. This value again is mapped against the above rules and will select the JSON or XML serialization. For example, if the client sends: Accept: text/xml;q=0.3, application/vnd.example.v3+json, the second (custom) mime type will be chosen, as its q-value is 1.0.
Extensible marshalling
The context may be extended to support marshalling from and to a given class. Builtin support for primitives, enums, value objects and arrays thereof exist, though you might want to handle your type specially.
<?phpclass Identifier extends Object {
publicfunction__construct($id) { ... }
publicfunctionintValue() { ... }
}
#[@webservice(path = '/resource')]
class TheService extends Object {
#[@inject]
publicfunctionchangeMarshalling(RestContext$context) {
$context->addSerializer(newinstance('TypeMarshaller<com.example.types.Identifier>', array(), '{ public function marshal(Identifier $instance) { return $instance->intValue(); } public function unmarshal($input) { return new Identifier((int)$input); } }');
}
}
?>
Payload versioning
Versioning is done via custom mime types, e.g.: application/vnd.example.v3+json. The version is simply part of the mime type string (here: "application/vnd.example.v3", and not handled in any special way). To overwrite a method with a different version, we use the "accepts" key of the @webmethod annotation for incoming payload and the "returns" key for returned data:
Keeping methods for all versions in one class encourages developers to clean up! If this is a definite problem, there is an alternative: Use the "accepts" key in the @webservice annotation:
If a given mime type cannot be matched directly, a method without "accepts" key is chosen (actually, its "accept" value is */*, which matches anything). If the acceptable mime type is not matched directly, a methd without "returns" key is chosen.
Methods can accept multiple mime types by using an array for the "accepts" key.
Client
Requirements
Common use-cases use simple source paths (e.g. "GET" is default method if omitted)
Authentication via Basic-Auth built-in 🆗
Optional type-casting to userland objects
Access to HTTP request and response headers 🆗
Built-in serialization to JSON and XML for all types 🆗
Extensible serialization and deserialization
Exception mapping
HTTP Errors => Exceptions as default 🆗
Default content type = JSON 🆗
Custom mime type support, graceful degradation
...
Current usage example:
<?php$client= newRestClient('http://api.example.com/');
$request= newRestRequest('/resource/{id}');
$request->addSegment('id', 5000); // Replaces token in resource$request->addParameter('details', 'true'); // POST or querystring$response= $client->execute($request);
$content= $response->content(); // Raw data as string$content= $response->data(); // Deserialize to map?>
Scope of Change
The goal is to create consistent and clean REST Client & Serverside APIs.
Rationale
After using the APIs for a while lots of deficiencies were noted.
Functionality
This document is divided into two sections - one of the serverside implementation, one on the client.
Server
Requirements
@webmethodfrom SOAP 🆗Current usage example:
Class-level changes
@webserviceannotation will allow an optional "path" key. If it supplies a value, it will be prepended to all "path" keys defined in methods.@paramfor query parameters,@pathfor path parameters,@headerfor headers,@cookiefor cookies. These annotations will be method-level annotations but written in a way to become forward-compatible with the parameter annotations described in RFC Parameter annotations #218.Responseinstance.The annotations are defined as follows:
The Response builder class:
Applying these changes to the above example would yield the following:
Serialization changes
When deserializing parameters from strings, the parameter type is taken into account. If the parameter type is a value object, the reflected class is checked for either a public one-arg constructor or a public static
valueOf()method.Example:
Exception mappers
To map exceptions, we will reuse the
Responseobject as seen above. Exception mappers can be registered by binding them to the context, which can be retrieved via the well-known@injectannotation.Example implementation:
By default, the following exceptions are mapped:
Mime type support
The default to map the mime type supplied with the client's
Content-Typerequest header as follows: The pattern*/jsonwill map to the JSON serialization and*/xmlto the XML serialization. This means, custom mime types such asapplication/vnd.example.v3+jsonwill work out of the box and will be mapped to the JSON serialization mechanism.The
Acceptheader will be parsed and returned media type will be determined by following the client-set preferences: The list is sorted by preference and the value with the highest preference then chosen. This value again is mapped against the above rules and will select the JSON or XML serialization. For example, if the client sends:Accept: text/xml;q=0.3, application/vnd.example.v3+json, the second (custom) mime type will be chosen, as its q-value is 1.0.Extensible marshalling
The context may be extended to support marshalling from and to a given class. Builtin support for primitives, enums, value objects and arrays thereof exist, though you might want to handle your type specially.
Payload versioning
Versioning is done via custom mime types, e.g.:
application/vnd.example.v3+json. The version is simply part of the mime type string (here: "application/vnd.example.v3", and not handled in any special way). To overwrite a method with a different version, we use the "accepts" key of the@webmethodannotation for incoming payload and the "returns" key for returned data:Keeping methods for all versions in one class encourages developers to clean up! If this is a definite problem, there is an alternative: Use the "accepts" key in the
@webserviceannotation:If a given mime type cannot be matched directly, a method without "accepts" key is chosen (actually, its "accept" value is
*/*, which matches anything). If the acceptable mime type is not matched directly, a methd without "returns" key is chosen.Methods can accept multiple mime types by using an array for the "accepts" key.
Client
Requirements
Current usage example:
Security considerations
Speed impact
Dependencies
For a forward-compatible way of handling parameter annotations, extension methods could be used as seen in https://github.com/thekid/xp-framework/compare/rfc218-fc
Related documents
XP Framework
Java
C#