CodeRigor

FREE LESSON

URLs, path segments & query encoding

Encode raw path segments and ordered query pairs without losing delimiters, repeats or blank values.

A URL has components with different delimiter rules. In https://api.example.test/v1/books?q=python#details, https is the scheme, api.example.test is the authority, /v1/books is the path, q=python is the query and details is the fragment. A fragment is client-side reference information and is not sent as part of an HTTP request target. This lab creates URLs with no fragment and performs no network operations.

The core distinction is data versus structure. If a user-supplied book ID is literally a/b, appending it without encoding creates two segments. Encode it as a%2Fb when the contract says it is one segment. Add real separator slashes outside the quoting operation. Percent-encoding preserves data in its particular component; it does not authorize a destination or guarantee that a server interprets all paths identically.

Keep one segment one segment
start / 2
Add structural slashes yourself; encode internal segment slashes as data.
Keep one segment one segment
Add structural slashes yourself; encode internal segment slashes as data.
Python
from urllib.parse import quote, urlencode

segment = quote("a/b c", safe="")
query = urlencode([("tag", "python"), ("tag", "http"), ("q", "a+b c")])
print(segment)  # a%2Fb%20c
print(query)    # tag=python&tag=http&q=a%2Bb+c

quote normally leaves / safe because callers often quote a whole path. Here each input is one segment, so explicitly set safe="". A path space becomes %20. Default urlencode uses the form-query convention: a space becomes +, while a literal plus becomes %2B. These are different contexts, not interchangeable global substitutions on a completed URL.

An ordered list of query pairs preserves repeated keys, blank values and caller order. Turning [("tag", "python"), ("tag", "http")] into a dictionary silently discards the first tag. A raw value "" still produces key=; missing and blank parameters may mean different things to an API. Leave those interpretations to its documented contract.

Query pairs retain every value
start / 2
urlencode receives the sequence directly; dictionary conversion would discard an earlier tag.
Query pairs retain every value
urlencode receives the sequence directly; dictionary conversion would discard an earlier tag.

The build takes a restricted, trusted base with an already encoded path prefix. It checks for http/https, a nonempty authority, no credentials and no nonempty query or fragment. urlsplit helps separate those components, while urlunsplit recombines them. This limited check is not comprehensive URL validation, a host allowlist or protection against every server-side request forgery scenario. No connection or DNS lookup occurs.

Raw appended segments and pairs are not pre-encoded. The literal raw text %2F becomes %252F; decoding it first would reinterpret data as a slash. By contrast, the supplied base path is already encoded and is preserved. Maintaining that boundary avoids accidental double interpretation.

Before coding, predict one segment a/b versus two segments a and b. Then add a blank value, duplicate tags, Unicode and ?, #, &, = inside data. Verify that only the separators you intentionally create remain literal. The fill supplies the module imports; the build asks you to keep component encoding, base checks and order preservation in one small pure function.

Use Python's urllib.parse reference for quote, urlencode, urlsplit and its explicit parsing-versus-validation limits, and RFC 3986 for URI components and percent-encoding.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Focus on targets in request lines and how a browser identifies a resource. Apply that model to the lesson’s separate path-segment and form-query encoding; the video is context, not a grader dependency.