Parameter Routing & Path Segment Parsing
Introduction
Starting from v1.0.0.420, HttpServer introduces two important features:
- Parameter Routing — Use
{param}syntax in URL paths to define dynamic parameter segments, similar to modern frameworks like Laravel, Express, etc. - PathInfoList — Split URL paths by
/into a collection object that supports access by index and by key
Used together, they make it easy to build RESTful-style APIs.
1. Parameter Routing
1.1 Basic Usage
When registering routes, use {paramName} to mark dynamic segments:
' Single parameter route
Call Server.Router.Add("/api/user/{id}", "UserController@Show", OnlyGet)
' Multi-parameter route
Call Server.Router.Add("/api/user/{userId}/post/{postId}", "PostController@Show", OnlyGet)
' Nested resource route
Call Server.Router.Add("/api/group/{groupId}/user/{userId}", "GroupCtrl@Detail", OnlyGet)1.2 Getting Parameters in Controllers
Access matched parameter values via the ctx.Request.RouteParams dictionary:
' Single parameter example: GET /api/user/123
Public Sub Show(ctx As cHttpServerContext)
Dim userId As String
userId = ctx.Request.RouteParams("id") ' Value is "123"
ctx.Response.Json Array("userId" & userId)
End Sub
' Multi-parameter example: GET /api/user/456/post/789
Public Sub ShowPost(ctx As cHttpServerContext)
Dim userId As String, postId As String
userId = ctx.Request.RouteParams("userId") ' Value is "456"
postId = ctx.Request.RouteParams("postId") ' Value is "789"
ctx.Response.Json Array(userId, postId)
End Sub1.3 RouteParams Property
Public RouteParams As New Scripting.Dictionary| Operation | Usage | Description |
|---|---|---|
| Get parameter | ctx.Request.RouteParams("id") | Returns parameter value string |
| Check existence | ctx.Request.RouteParams.Exists("id") | Returns Boolean |
| Parameter count | ctx.Request.RouteParams.Count | 0 when no parameter route matched |
| Iterate parameters | For Each k In RouteParams.Keys | Iterate all parameter names |
Note: If no parameter route is matched,
RouteParamsis an empty dictionary (Count = 0).
1.4 Matching Rules
| Rule | Description | Example |
|---|---|---|
Parameter segment {name} | Matches any non-empty string | /api/user/{id} matches /api/user/123 |
| Fixed segment | Must match exactly (case-insensitive) | In /api/user/{id}, api and user are fixed segments |
| Same segment count | Path segment count must match pattern segment count | /api/user/{id} is 3 segments, won't match /api/user/123/edit (4 segments) |
| Parameter non-empty | Parameter segment cannot match empty value | /api/user/ does not match /api/user/{id} |
Matching Examples:
| Route Pattern | Request Path | Match Result | RouteParams |
|---|---|---|---|
/api/user/{id} | /api/user/123 | ✅ | {"id": "123"} |
/api/user/{id} | /api/user/abc | ✅ | {"id": "abc"} |
/api/user/{id} | /api/user/ | ❌ Segment count mismatch | — |
/api/user/{id} | /api/users/123 | ❌ Fixed segment mismatch | — |
/api/user/{userId}/post/{postId} | /api/user/456/post/789 | ✅ | {"userId": "456", "postId": "789"} |
1.5 Matching Priority
Route matching executes in the following priority order:
- Exact match — O(1) dictionary lookup, highest performance. e.g.,
/api/usersmatches directly - ANY exact match — Look up in the ANY method dictionary
- Parameter route match — Traverse route items containing
{param}, match segment by segment
Exact routes always take priority over parameter routes; parameter wildcard won't cause false matches.
' Register both exact and parameter routes
Call Server.Router.Add("/api/user/me", "User@Me", OnlyGet) ' Exact match first
Call Server.Router.Add("/api/user/{id}", "User@Show", OnlyGet) ' Parameter route second
' GET /api/user/me → Matches exact route, calls User@Me
' GET /api/user/123 → Matches parameter route, RouteParams("id") = "123"1.6 Parameter Routing vs Query Parameters
| Comparison | Parameter Routing | Query Parameters |
|---|---|---|
| URL format | /api/user/123 | /api/user?id=123 |
| Access method | ctx.Request.RouteParams("id") | ctx.Request.QueryString("id") |
| RESTful style | ✅ Conforms | ❌ Does not conform |
| SEO friendly | ✅ More friendly | Average |
| Optional parameters | Segment count must match | Naturally optional |
| Use case | Resource identification, hierarchical paths | Filtering, pagination, sorting |
Both can be combined:
' GET /api/user/123/posts?page=2&limit=10
Call Server.Router.Add("/api/user/{id}/posts", "Post@List", OnlyGet)
Public Sub List(ctx As cHttpServerContext)
Dim userId As String: userId = ctx.Request.RouteParams("id")
Dim page As String: page = ctx.Request.QueryString("page")
Dim limit As String: limit = ctx.Request.QueryString("limit")
End Sub1.7 Complete RESTful API Example
' Register controller
Call Server.Router.Reg("Product", New cProductController)
' List: GET /products
Call Server.Router.Add("/products", "Product@List", OnlyGet)
' Detail: GET /products/123
Call Server.Router.Add("/products/{id}", "Product@Detail", OnlyGet)
' Create: POST /products
Call Server.Router.Add("/products", "Product@Create", OnlyPost)
' Update: PUT /products/123
Call Server.Router.Add("/products/{id}", "Product@Update", OnlyPut)
' Delete: DELETE /products/123
Call Server.Router.Add("/products/{id}", "Product@Delete", OnlyDelete)
' Sub-resource: GET /products/123/reviews
Call Server.Router.Add("/products/{id}/reviews", "Product@Reviews", OnlyGet)
' Sub-resource detail: GET /products/123/reviews/456
Call Server.Router.Add("/products/{productId}/reviews/{reviewId}", "Product@ReviewDetail", OnlyGet)1.8 Internal Implementation
Parameter routing is implemented by the cHttpServerRouteItem class, with the core mechanism:
- On registration: The
Initmethod splits the route pattern into a path segment array and extracts{param}parameter names - On matching: The
Matchmethod compares segment by segment — fixed segments match exactly (case-insensitive), parameter segments match any non-empty value - Parameter extraction: After a successful match, parameter names and values are written to the
Request.RouteParamsdictionary
Route pattern: /api/user/{id}/post/{postId}
↓ Init() split
Segment array: ["api", "user", "{id}", "post", "{postId}"]
Parameter names: ["id", "postId"]
Request path: /api/user/123/post/456
↓ Match() segment-by-segment matching
"api" = "api" ✅ Fixed segment match
"user" = "user" ✅ Fixed segment match
"{id}" = "123" ✅ Parameter segment → RouteParams("id") = "123"
"post" = "post" ✅ Fixed segment match
"{postId}" = "456" ✅ Parameter segment → RouteParams("postId") = "456"2. PathInfoList Path Segment Collection
2.1 Introduction
PathInfoList is a cCollection type collection object that automatically splits the URL path by / during request parsing. Each segment is stored in the collection where both key and value are the segment name, supporting access by index and by key.
Public PathInfoList As New cCollection2.2 Usage Examples
' Request: GET /api/user/list?page=1
' ── Access by index (1-based) ──
MsgBox ctx.Request.PathInfoList(1) ' → "api"
MsgBox ctx.Request.PathInfoList(2) ' → "user"
MsgBox ctx.Request.PathInfoList(3) ' → "list"
' ── Get path segment count ──
MsgBox ctx.Request.PathInfoList.Count ' → 3
' ── Access by key (key=value=segment name) ──
MsgBox ctx.Request.PathInfoList("api") ' → "api"
' ── Check if path contains a segment ──
If ctx.Request.PathInfoList.Exists("api") Then
Debug.Print "Path contains 'api' segment"
End If
' ── Iterate all path segments ──
Dim i As Long
For i = 1 To ctx.Request.PathInfoList.Count
Debug.Print "Segment" & i & ": " & ctx.Request.PathInfoList(i)
Next i2.3 PathInfoList for Common Paths
| Request Path | PathInfoList.Count | PathInfoList(1) | PathInfoList(2) | PathInfoList(3) |
|---|---|---|---|---|
/ | 0 | — | — | — |
/api | 1 | "api" | — | — |
/api/user | 2 | "api" | "user" | — |
/api/user/list | 3 | "api" | "user" | "list" |
/api/user/123 | 3 | "api" | "user" | "123" |
2.4 Typical Use Cases
' Check if it's an API request
If ctx.Request.PathInfoList.Count > 0 Then
If ctx.Request.PathInfoList(1) = "api" Then
' Handle API request
End If
End If
' Get resource ID from path (parameter routing is better for this)
If ctx.Request.PathInfoList.Count >= 3 Then
If ctx.Request.PathInfoList(2) = "user" Then
Dim userId As String
userId = ctx.Request.PathInfoList(3) ' e.g. "123"
End If
End If
' Recommended: Use parameter routing to get IDs instead:
' userId = ctx.Request.RouteParams("id")Tip: For getting dynamic parameters from the path (like user IDs), prefer using parameter routing's
RouteParams— it's more semantic and type-safe.
3. New Files List
| File | Type | Description |
|---|---|---|
cHttpServerRouteItem.cls | New | Route item class: parses {param} patterns, matches paths, extracts parameters |
cHttpServerRouter.cls | Modified | Route matching logic: exact match first, parameter route second |
cHttpServerRequest.cls | Modified | Added RouteParams (Dictionary) and PathInfoList (cCollection) |
100% backward compatible — existing exact route registration and matching are not affected.
Last Updated: 2026-06-11