www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - typed-router: Django-esque Path Handling for vibe.d

reply Kyle Ingraham <kyle kyleingraham.com> writes:
Hi all. I use Django and Python for web development in my day job 
but vastly prefer working in D. I decided to try using D's 
flexibility to bring a bit of Django's API to vibe.d's routing. 
The result is a vibe.d router that implements Django's URL 
dispatching system.

The package is available via dub here: 
https://code.dlang.org/packages/typed_router.

Below are the contents of the README explaining a bit about how 
it works. I welcome any and all feedback!


A [vibe.d](https://vibed.org/) router that implements 
[Django's](https://www.djangoproject.com/) URL dispatching system.

```d
import typed_router : TypedURLRouter;

import vibe.core.core;
import vibe.http.server;
import vibe.http.status;

int main()
{
     auto router = new TypedURLRouter!();
     router.get!"/hello/<name>/<int:age>/"(&helloUser);

     auto settings = new HTTPServerSettings;
     settings.bindAddresses = ["127.0.0.1"];
     settings.port = 9000;

     auto listener = listenHTTP(settings, router);
     scope (exit)
     listener.stopListening();

     return runApplication();
}

void helloUser(HTTPServerRequest req, HTTPServerResponse res, 
string name, int age)  safe
{
     import std.conv : to;

     res.contentType = "text/html; charset=UTF-8";
     res.writeBody(`
<!DOCTYPE html>
<html lang="en">
     <head></head>
     <body>
         Hello, ` ~ name ~ `. You are ` ~ to!string(age) ~ ` years 
old.
     </body>
</html>`,
     HTTPStatus.ok);
}
```


typed-router uses D's flexibility to implement key components of 
Django's URL dispatching system. The end result is a
blending of the ergonomics available in Django with the, to me, 
superior development experience of D.

Key components of Django's [URL dispatching 
system](https://docs.djangoproject.com/en/dev/topics/http/urls/#url-dispatcher)
are:
- The URL path expression scheme
- The ability to extend the path expression scheme through path 
converters


Django allows the developer to [specify values to be 
captured](https://docs.djangoproject.com/en/dev/topics/http/urls/#example).
This is similar to functionality available in most web frameworks 
(including vibe.d). Identifiers in angle brackets will be used to 
extract
values from matched paths. Those values are then made available 
to handlers as strings. After matching the following
example path on structure, Django would make `name` and `age` 
string values available to the path's associated handler:

```python
"/hello/<name>/<age>/"
```

Where things get interesting is Django's URL path expression 
scheme's path converters.


Captured value specifications can optionally include a path 
converter. Path converters influence both how their portion
of the path is matched when routing, and the type of value passed 
to an associated handler. Take the following path as
an example:

```python
"/hello/<name>/<int:age>/"
```

`name` has no path converter and so would be matched as a string. 
`age` on the other hand has the `int` path converter
which matches against integers and passes an integer value to the 
path's handler. A request to `/hello/ash/12/` would
match against this path while a request to `/hello/ash/twelve/` 
would not.

Behind the scenes, path converters are objects that:
- Hold a regex pattern for values they match against
- Understand how to convert string values to the path converter's 
return type


typed-router provides `TypedURLRouter` which is a vibe.d router 
that understands Django's URL path expression scheme.
Paths are parsed at compile-time using built-in or user-provided 
path converters. Built-in path converters match
[Django's built-in 
set](https://docs.djangoproject.com/en/dev/topics/http/urls/#path-converters).
User-specified path
converters must first be defined as structs with the following 
properties:

- An `enum` member named `regex` with a regex character class 
representing strings to match against within a requested path.
- A ` safe` `toD` function that accepts a `const string`. The 
return type can be any desired outside `void`. This function 
converts strings to the type produced by the path converter.



```d
import typed_router : bindPathConverter, TypedURLRouter;

import vibe.core.core;
import vibe.http.server;
import vibe.http.status;

struct NoNinesIntConverter
{
     enum regex = "[0-8]+"; // Ignores '9'

     int toD(const string value)  safe
     {
         import std.conv : to;

         return to!int(value);
     }
}

int main()
{
     auto router = new 
TypedURLRouter!([bindPathConverter!(NoNinesIntConverter, 
"nonines")]);
     router.get!"/hello/<name>/<nonines:age>/"(&helloUser);

     auto settings = new HTTPServerSettings;
     settings.bindAddresses = ["127.0.0.1"];
     settings.port = 9000;

     auto listener = listenHTTP(settings, router);
     scope (exit)
     listener.stopListening();

     return runApplication();
}

void helloUser(HTTPServerRequest req, HTTPServerResponse res, 
string name, int age)  safe {
     import std.conv : to;

     res.contentType = "text/html; charset=UTF-8";
     res.writeBody(`
<!DOCTYPE html>
<html lang="en">
     <head></head>
     <body>
         Hello, ` ~ name ~ `. You are ` ~ to!string(age) ~ ` years 
old.
     </body>
</html>`,
     HTTPStatus.ok);
}
```


Handlers given to `TypedURLRouter` (like with `URLRouter`) should 
at the very least return `void` and accept an
`HTTPServerRequest` and an `HTTPServerResponse`. Values extracted 
from the request's path are saved to
`HTTPServerRequest.params` as strings.

If the parameter signature for a handler is extended with the 
types returned by its path's path converters then
`TypedURLRouter` will additionally use the path converters' `toD` 
functions to pass converted values to the handler.



- Middleware (there is currently no way to specify handlers to be 
called for every path)
- Matching the API for vibe.d's `URLRouter`
     - Set of valid handler signatures
     - Handler registration functions e.g. `post`
     - Per-router path prefixes
Oct 01 2022
parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Saturday, 1 October 2022 at 18:29:23 UTC, Kyle Ingraham wrote:
 Hi all. I use Django and Python for web development in my day 
 job but vastly prefer working in D. I decided to try using D's 
 flexibility to bring a bit of Django's API to vibe.d's routing. 
 The result is a vibe.d router that implements Django's URL 
 dispatching system.

 [...]
Nice!
Oct 02 2022