vibe.web.rest

Automatic high-level RESTful client/server interface generation facilities.

This modules aims to provide a typesafe way to deal with RESTful APIs. D's interfaces are used to define the behavior of the API, so that they can be used transparently within the application. This module assumes that HTTP is used as the underlying transport for the REST API.

While convenient means are provided for generating both, the server and the client side, of the API from a single interface definition, it is also possible to use as a pure client side implementation to target existing web APIs.

The following paragraphs will explain in detail how the interface definition is mapped to the RESTful API, without going into specifics about the client or server side. Take a look at registerRestInterface and RestInterfaceClient for more information in those areas.

These are the main adantages of using this module to define RESTful APIs over defining them manually by registering request handlers in a URLRouter:

  • Automatic client generation: once the interface is defined, it can be used both by the client side and the server side, which means that there is no way to have a protocol mismatch between the two.
  • Automatic route generation for the server: one job of the REST module is to generate the HTTP routes/endpoints for the API.
  • Automatic serialization/deserialization: Instead of doing manual serialization and deserialization, just normal statically typed member functions are defined and the code generator takes care of converting to/from wire format. Custom serialization can be achieved by defining JSON or string parameters/return values together with the appropriate @bodyParam annotations.
  • Higher level representation integrated into D: Some concepts of the interfaces, such as optional parameters or in/out/ref parameters, as well as Nullable!T, are translated naturally to the RESTful protocol.

The most basic interface that can be defined is as follows:

@path("/api/")
interface APIRoot {
    string get();
}

This defines an API that has a single endpoint, 'GET /api/'. So if the server is found at http://api.example.com, performing a GET request to $(CODE http://api.example.com/api/) will call the get() method and send its return value verbatim as the response body.

Public Imports

vibe.web.common
public import vibe.web.common;
Undocumented in source.

Members

Aliases

RestErrorHandler
alias RestErrorHandler = void delegate(HTTPServerRequest, HTTPServerResponse, RestErrorInformation error) @(safe)
Undocumented in source.
after
alias after = vibe.internal.meta.funcattr.after

Allows processing the return value of a handler method and the request/response objects.

before
alias before = vibe.internal.meta.funcattr.before

Allows processing the server request/response before the handler method is called.

Classes

RestInterfaceClient
class RestInterfaceClient(I)

Implements the given interface by forwarding all public methods to a REST server.

RestInterfaceSettings
class RestInterfaceSettings

Encapsulates settings used to customize the generated REST interface.

Functions

d
int d(InterfaceProxy!(InputStream) )
Undocumented in source. Be warned that the author may not have intended to support it.
generateRestJSClient
void generateRestJSClient(R output, RestInterfaceSettings settings)

Generates JavaScript code to access a REST interface from the browser.

getInterfaceValidationError
string getInterfaceValidationError()
Undocumented in source.
get_matching_content_type
int get_matching_content_type(string req_content_types_str)

The client sends the list of allowed content types in the 'Allow' http header and the response will contain a 'Content-Type' header. This function will try to find the best matching @SerializationResult UDA based on the allowed content types.

registerRestInterface
URLRouter registerRestInterface(URLRouter router, TImpl instance, RestInterfaceSettings settings)
URLRouter registerRestInterface(URLRouter router, TImpl instance, MethodStyle style)
URLRouter registerRestInterface(URLRouter router, TImpl instance, string url_prefix, MethodStyle style)

Registers a server matching a certain REST interface.

s
void s(OutputRange!char , int )
Undocumented in source. Be warned that the author may not have intended to support it.
serveRestJSClient
HTTPServerRequestDelegate serveRestJSClient(RestInterfaceSettings settings)
HTTPServerRequestDelegate serveRestJSClient(URL base_url)
HTTPServerRequestDelegate serveRestJSClient(string base_url)
HTTPServerRequestDelegate serveRestJSClient()

Returns a HTTP handler delegate that serves a JavaScript REST client.

stripTUnderscore
string stripTUnderscore(string name, RestInterfaceSettings settings)
Undocumented in source. Be warned that the author may not have intended to support it.
test1
int test1()
Undocumented in source.
test2
int test2()
Undocumented in source.

Structs

Collection
struct Collection(I)

Models REST collection interfaces using natural D syntax.

RestErrorInformation
struct RestErrorInformation
Undocumented in source.

Templates

GenCmp
template GenCmp(string name, int id, string cmpTo)
Undocumented in source.

Detailed Description

Endpoint generation

An endpoint is a combination of an HTTP method and a local URI. For each public method of the interface, one endpoint is registered in the URLRouter.

By default, the method and URI parts will be inferred from the method name by looking for a known prefix. For example, a method called getFoo will automatically be mapped to a 'GET /foo' request. The recognized prefixes are as follows:

PrefixHTTP verb
getGET
queryGET
setPUT
putPUT
updatePATCH
patchPATCH
addPOST
createPOST
postPOST

Member functions that have no valid prefix default to 'POST'. Note that any of the methods defined in vibe.http.common.HTTPMethod are supported through manual endpoint specifications, as described in the next section.

After determining the HTTP method, the rest of the method's name is then treated as the local URI of the endpoint. It is expected to be in standard D camel case style and will be transformed into the style that is specified in the call to registerRestInterface, which defaults to MethodStyle.lowerUnderscored.

Manual endpoint specification

Endpoints can be controlled manually through the use of @path and @method annotations:

@path("/api/")
interface APIRoot {
    // Here we use a POST method
    @method(HTTPMethod.POST)
	// Our method will located at '/api/foo'
	@path("/foo")
	void doSomething();
}

Manual path annotations also allows defining custom path placeholders that will be mapped to function parameters. Placeholders are path segments that start with a colon:

@path("/users/")
interface UsersAPI {
    @path(":name")
    Json getUserByName(string _name);
}

This will cause a request "GET /users/peter" to be mapped to the getUserByName method, with the _name parameter receiving the string "peter". Note that the matching parameter must have an underscore prefixed so that it can be distinguished from normal form/query parameters.

It is possible to partially rely on the default behavior and to only customize either the method or the path of the endpoint:

@method(HTTPMethod.POST)
void getFoo();

In the above case, as 'POST' is set explicitly, the route would be 'POST /foo'. On the other hand, if the declaration had been:

@path("/bar")
void getFoo();

The route generated would be 'GET /bar'.

Properties: @property functions have a special mapping: property getters (no parameters and a non-void return value) are mapped as GET functions, and property setters (a single parameter) are mapped as PUT. No prefix recognition or trimming will be done for properties.

Method style

Method names will be translated to the given 'MethodStyle'. The default style is MethodStyle.lowerUnderscored, so that a function named getFooBar will match the route 'GET /foo_bar'. See vibe.web.common.MethodStyle for more information about the available styles.

Serialization: By default the return values of the interface methods are serialized as a JSON text and sent back to the REST client. To override this, you can use the @resultSerializer attribute

struct TestStruct {int i;}

interface IService {
@safe:
	@resultSerializer!(
		// output_stream implements OutputRange
		function (output_stream, test_struct) {
			output_stream ~= serializeToJsonString(test_struct);
		},
		// input_stream implements InputStream
		function (input_stream) {
			return deserializeJson!TestStruct(input_stream.readAllUTF8());
		},
		"application/json")()
	@resultSerializer!(
		// output_stream implements OutputRange
		function (output_stream, test_struct) {
			output_stream ~= test_struct.i.to!string();
		},
		// input_stream implements InputStream
		function (input_stream) {
			TestStruct test_struct;
			test_struct.i = input_stream.readAllUTF8().to!int();
			return test_struct;
		},
		"plain/text")()
	TestStruct getTest();
}

class Service : IService {
@safe:
	TestStruct getTest() {
		TestStruct test_struct = {42};
		return test_struct;
	}
}

Serialization policies

You can customize the serialization of any type used by an interface by using serialization policies. The following example is using the Base64ArrayPolicy, which means if X contains any ubyte arrays, they will be serialized to their base64 encoding instead of their normal string representation (e.g. "[1, 2, 255]").

@serializationPolicy!(Base64ArrayPolicy)
interface ITestBase64
{
	@safe X getTest();
}

See Also

To see how to implement the server side in detail, jump to registerRestInterface.

To see how to implement the client side in detail, jump to the RestInterfaceClient documentation.

Meta

License

Subject to the terms of the MIT license, as written in the included LICENSE.txt file.

Authors

Sönke Ludwig, Михаил Страшун, Mathias 'Geod24' Lang