URI Templates

WellRESTed allows you to register middleware with a router using URI Templates, based on the URI Templates defined in RFC 6570. These templates include variables (enclosed in curly braces) which are extracted and made available to the dispatched middleware.

Reading Variables

Basic Usage

Register middleware with a URI Template by providing a path that include at least one section enclosed in curly braces. The curly braces define variables for the template.

$router->register("GET", "/widgets/{id}", $widgetHandler);

The router will match requests for paths like /widgets/12 and /widgets/mega-widget and dispatch $widgetHandler with the extracted variables made available as request attributes.

To read a path variable, the $widgetHandler middleware inspects the request attribute named "id", since id is what appears inside curly braces in the URI template.

$widgetHandler = function ($request, $response, $next) {
    // Read the variable extracted form the path.
    $id = $request->getAttribute("id");
};

When the request path is /widgets/12, the value returned by $request->getAttribute("id") is "12". For /widgets/mega-widget, the value is "mega-widget".

Note

Request attributes are a feature of the ServerRequestInterface provided by PSR-7.

Multiple Variables

The example above included one variable, but URI Templates may include multiple variables. Each variable will be provided as a request attribute, so be sure to give your variables unique names.

Here’s an example with a handful of variables. Suppose we have a template describing the path for a user’s avatar image. The image is identified by a username and the image dimensions.

$router->register("GET", "/avatars/{username}-{width}x{height}.jpg", $avatarHandlers);

A request for GET /avatars/zoidberg-100x150.jpg will provide these request attributes:

$avatarHandlers = function ($request, $response, $next) {
    // Read the variables extracted form the path.
    $username = $request->getAttribute("username");
    // "zoidberg"
    $width = $request->getAttribute("width");
    // "100"
    $height = $request->getAttribute("height");
    // "150"
};

Arrays

You may also match a comma-separated series of values as an array using a URI Template by providing a * at the end of the variable name.

$router->register("GET", "/favorite-colors/{colors*}", $colorsHandler);

A request for GET /favorite-colors/red,green,blue will provide an array as the value for the "colors" request attribute.

$colorsHandler = function ($request, $response, $next) {
    // Read the variable extracted form the path.
    $colorsList = $request->getAttribute("colors");
    /*  Array
        (
            [0] => red
            [1] => green
            [2] => blue
        )
    */
};

Matching Characters

Unreserved Characters

By default, URI Template variables will match only “unreserved” characters. RFC 3968 Section 2.3 defines unreserved characters as alphanumeric characters, -, ., _, and ~. All other characters must be percent encoded to be matched by a default template variable.

Note

Percent-encoded characters matched by template variables are automatically decoded when provided as request attributes.

Given the template /users/{user}, the following paths provide these values for getAttribute("user"):

Paths and Values for the Template /users/{user}
Path Value
/users/123 “123”
/users/zoidberg “zoidberg”
/users/zoidberg%40planetexpress.com zoidberg@planetexpress.com

A request for GET /uses/zoidberg@planetexpress.com will not match this template, because @ is not an unreserved character and is not percent encoded.

Reserved Characters

If you need to match a non-percent-encoded reserved character like @ or /, use the + operator at the beginning of the variable name.

Using the template /users/{+user}, we can match all of the paths above, plus /users/zoidberg@planetexpress.com.

Reserved matching also allows matching unencoded slashes (/). For example, given this template:

$router->register("GET", "/my-favorite-path{+path}", $pathHandler);

The router will dispatch $pathHandler with for a request to GET /my-favorite-path/has/a/few/slashes.jpg

$pathHandler = function ($request, $response, $next) {
    // Read the variable extracted form the path.
    $path = $request->getAttribute("path");
    // "/has/a/few/slashes.jpg"
};

Note

Combine the + operator and * modifier to match reserved characters as an array. For example, the template /{+vars*} will match the path /c@t,d*g, providing the array ["c@t", "d*g"].