Skip to content
Lesson 6 of 16

Step 1 of 5 · Reading · ~3 min

Read

Routing & Responses

Path Parameters & Query Strings

/users/42/posts/7 is not a route anyone registers literally, because there are a billion possible ids. Real routers match patterns (/users/{id}/posts/{post}) and hand the handler the extracted variables. Add the query string (?page=2&sort=desc) and you have the two channels through which every web API receives input. This lesson builds both extractors.

Pattern routes: match by segments

Split the pattern and the request path on / and walk them side by side:

pattern  /users/{id}/posts/{post}  ->  users  {id}  posts  {post}
request  /users/42/posts/7         ->  users  42    posts  7

A literal segment must be equal. A {name} segment matches any single non-empty segment and captures it. The segment counts must be equal, so /users/42 cannot match the four-segment pattern. Captured values are strings: the router does not know that {id} is numeric. Whether /users/abc is a 404 or a 400 is the handler's decision.

Splitting keeps the shape honest. / is one empty segment, /users/ is users and an empty one, and /users//posts has an empty segment in the middle that a {id} must refuse to capture.

Decode after you split

Percent-decode each segment after splitting on /, not before. Then %2F stays inside its segment:

/files/a%2Fb.txt   split ->  files  a%2Fb.txt   decode ->  files  a/b.txt

Decode first and the same request splits into three segments, and a parameter could never contain a slash it was explicitly given. A malformed escape in any segment is 400.

Which route wins?

/users/new and /users/{id} both match /users/new. The rule nearly every framework converged on: the most specific route wins, regardless of registration order. Otherwise adding a pattern silently steals an existing literal route. This course defines specificity position by position, left to right: at the first segment where two candidates differ, the literal beats the parameter. For /a/b/c, the route /a/b/{y} beats /a/{x}/c. If two patterns have exactly the same shape, the one registered first wins.

Only routes that match the request method compete. If the path matches some route but no route for this method, the answer is 405 Allow: ... with the methods those routes carry, sorted alphabetically. If no route matches the path at all, 404.

The query string

Everything after the first ? never takes part in routing. It is parsed separately into key=value pairs:

?q=hello+world&tag=a%26b&empty=&bare&q=second
 q="hello world"  tag="a&b"  empty=""  bare=""  q="second"
  • Pairs are separated by &; an empty piece is ignored, and so is a pair with an empty key.
  • Split each pair at the first =. A piece with no = is a key with the empty value.
  • + means space and %XX is a byte, in keys and values alike. Here a malformed escape is left as typed (100% stays 100%), because a lenient query parser is the norm.
  • A repeated key keeps every value, in order of appearance.

Output

For a match print the handler, then each path parameter as :name="value" sorted by name, then each query pair as ?key="value" sorted by key (equal keys keep their order of appearance). Everything is separated by single spaces.

Common mistakes

  • Decoding the whole path first, so an encoded slash splits a parameter.
  • Letting {id} match an empty segment.
  • Choosing by registration order, so /users/{id} shadows /users/new.
  • Collapsing ?tag=a&tag=b into one value.
  • Treating + as a plus sign.

Your exercise

Same input shape as the last lesson: a route table, an empty line, then request lines. The starter splits the input and prints your answers. You match, rank, extract and format.

Up nextKeep-Alive ConnectionsConcurrency & Persistence

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…