My web.d
file is an add-on module for cgi.d
that utilizes D's reflection and code generation capabilities to automate tasks such as URL routing and JSON and HTML generation from your code.
The web.d
file has several dependencies that can be found in my Github repository. You'll need to download each of the files and compile them all together. Download cgi.d
, web.d
, sha.d
, dom.d
, and characterencodings.d
to your project's directory.
Let's execute the following steps to create a web API:
arsd.web
.ApiProvider
.export
methods, which implement various functions that you want to expose.main
function from the library with mixin FancyMain!Your_Class_name;
.cgi.d
or web.d
. We can compile with the following command to use the embedded HTTP server and disable the automatic creation of session files:dmd yourfile.d cgi.d web.d sha.d dom.d characterencodings.d -version=no_automatic_session -version=embedded_httpd
The code is as follows:
import arsd.web; class Test : ApiProvider { export int add(int a, int b) { return a+b; } export string sayHello(string name) { return "Hello, " ~ name; } } mixin FancyMain!Test;
If you go to http://localhost:8085/add
or http://localhost:8085/say-hello
, you will see the automatic form and can fill it out to see the result of the corresponding function. You can see the machine-consumable result by going to http://localhost:8085/add?a=1&b=2&envelopeFormat=json&format=json
.
The implementation of web.d
was one of my earliest explorations of compile-time reflection and code generation, and as such, its code is not very pretty to look at, but the results are remarkable.
Using __traits(derivedMembers)
, web.d
inspects each method in your ApiProvider
subclass, looking for other compatible types and functions marked with the export protection level (checking with __traits(getProtection)
).
It recursively scans compatible types, for example, other ApiProvider
instances, to prepare a tree of URL-mapped functions and objects. The export
methods anywhere in the tree are made available through the Web by automatically generating a wrapper function for them.
The wrapper function translates an associative array of parameters from the web request into the typed arguments expected by the method. Then, the method is run and the return value is automatically formatted based on an optional pair of URL parameters: format
and envelopeFormat
.
The format
parameter controls the format of the data (for example, html
, json
, or table
) and envelopeFormat
controls the format of the surrounding metadata (possible options include html
, which wraps the content in an HTML document for presentation in the browser, and json
, which is meant for machine consumption). The wrapper function also catches any exceptions thrown by the method and also forwards them to the API client or web browser.
The formatting implementation uses compile-time reflection paired with conditional compilation to automatically handle most types. If the necessary code to support a format does not compile, attempting to use that format will throw an exception at runtime.
The web.d
module also uses the information it gathers from compile-time reflection to automatically generate HTML forms that can be used as a prototype UI for the functions.
The web.d
module also includes an HTML template engine that extends dom.d
(which we'll use in the next recipe), an ApiObject
type to create RESTful
resources, and web session support that can be file- or cookie-backed. Moreover, web.d
works well with html.d
. Also, my Github repository provides methods for HTML sanitization (done via the DOM parser and a whitelist of allowed tags), CSS denesting and macro expansion similar to the SCSS project, and more. However, these modules aren't very well documented at this time, however, so if you want to experiment with them, look at the source code.
18.227.190.211