url
Parse, build, join and escape URLs and query strings without string surgery.
import "std:url"std:url takes URLs apart and puts them back together. Reach for it whenever a URL is more than a string you pass along: reading a query parameter, adding one, resolving a link against a base, or escaping user input before it goes into an address. Building URLs with ++ works until a value contains a space or an ampersand.
Parse a URL#
url.parse(text) returns a result holding the parts. A URL that can't be parsed is an error, not a half-filled record:
import "std:url"
auto u = url.parse("https://ada:[email protected]:8443/a/b%20c?x=1&y=two+words&x=2#top").unwrap()
log u.scheme u.hostname u.port
log u.host
log u.user u.password
log u.path
log u.rawPath
log u.queryString
log u.fragment
log url.parse("http://[bad").isErr()Try changing the port, or removing the #top, and run it again: the parts that are missing come back as empty strings.
host includes the port and hostname doesn't. path is decoded and rawPath is as it was written. The parts that are absent come back as empty strings.
Query parameters#
u.query is the query string already decoded into an object. A parameter that appears once is a string, and one that repeats is an array of strings. To read one parameter from a URL string directly, use url.param, which returns null for a parameter that isn't there:
import "std:url"
auto u = url.parse("https://example.com/s?x=1&y=two+words&x=2").unwrap()
log u.query
string link = "https://example.com/s?q=hello&n=2"
log url.param(link, "q").unwrap()
log url.param(link, "missing").unwrap()url.parseQuery("a=1&b=2") does the same for a bare query string, with or without the leading ?. url.stringifyQuery goes the other way, with keys sorted so the result is stable, and an array becomes a repeated key:
import "std:url"
log url.parseQuery("?a=1&b=two+words&a=3").unwrap()
log url.stringifyQuery({b: 2, a: "x y", list: [1, 2]})Set parameters on a URL#
url.withParams(url, params) returns the URL with the parameters set. Existing ones with the same name are replaced and the rest are kept:
import "std:url"
string next = url.withParams("https://example.com/s?q=1&keep=yes", {q: "new", page: 2}).unwrap()
log nextThis is the safe way to add a user's search text to a link: every value is escaped for you.
Escaping#
url.escape makes text safe to use as one query key or value, and url.unescape reverses it (a + becomes a space). Unescaping can fail on a broken % sequence, so it returns a result:
import "std:url"
log url.escape("a b&c=d/é")
log url.unescape("a%20b+c").unwrap()
log url.unescape("100%").isErr()Join and build#
url.join(base, reference) resolves a link the way a browser does, so a page can turn ../api into a full address. url.build assembles a URL from the same parts parse returns:
import "std:url"
log url.join("https://example.com/docs/guide/", "../api?v=2").unwrap()
log url.join("https://example.com/docs/guide", "/root").unwrap()
log url.build({scheme: "https", host: "example.com", path: "/a b", query: {k: "v"}, fragment: "f"})Checking input#
url.isValid(text) is true only for an absolute URL with a scheme and a host, so a relative path or a bare domain is rejected:
import "std:url"
for text in ["https://example.com", "example.com", "/relative", "ftp://files.example.com/a"] (
log text url.isValid(text)
)A valid URL can still use a scheme you don't want. If a form accepts links, also check scheme is http or https before following one.
Why use url.withParams instead of joining "?q=" ++ text onto a URL?
Next page link
Given a search URL, print the same URL with page set to 2. Keep the existing q.
https://example.com/search?page=2&q=osl
API reference#
12 members, generated from the std:url source of OSL 9.3.0.
Functions
url.build(object parts) stringbuild assembles a URL from the parts url.parse returns. Accepted keys: scheme, host, or hostname with port, path, rawPath, query (an object whose array values repeat the key) or queryString, fragment, and user with password. Every key is optional; an unknown key throws ValueError. The valid key of url.parse's result is ignored, so a parsed URL builds back.
url.decode(any query) objectDeprecated. Deprecated: use url.parseQuery(text) instead, which fails with ParseError for malformed input.
decode reads a query string into an object, or {} when it is malformed.
url.encode(object m) stringDeprecated. Deprecated: use url.stringifyQuery(params) instead.
encode encodes an object as a query string.
url.escape(string text) stringescape percent-encodes text for use as a query key or value, writing spaces as "+".
url.isValid(string text) booleanisValid reports whether text is an absolute URL with a scheme and a host, such as "https://example.com".
url.join(string base, string ref) result<string, error>join resolves a relative reference against a base URL, as a browser resolves a link: join("https://x.com/a/", "b") is "https://x.com/a/b". The result always stays on base's origin, so a ref that names its own scheme or host (such as "https://other.example" or "//other.example") fails with ValueError. Fails with ParseError when either URL is malformed.
url.param(string url, string key) result<string?, error>param returns the first value of a query parameter in url, or null when the parameter is absent. Fails with ParseError when url is malformed.
url.parse(string text) result<{fragment: string, host: string, hostname: string, password: string, path: string, port: string, query: object, queryString: string, rawPath: string, scheme: string, user: string, valid: boolean (deprecated)}, error>parse splits a URL into its components. It returns {scheme, host, hostname, port, path, rawPath, query, queryString, fragment, user, password}: every key is a string ("" when the URL has no such part) except query, an object mapping each query key to its value, or to an array of values when the key repeats. path is decoded and rawPath keeps its escapes. The deprecated key valid is always true. Relative references such as "../a" parse too. Fails with ParseError for malformed input such as a bad percent-escape or port.
url.parseQuery(string text) result<object, error>parseQuery reads a query string such as "a=1&b=two+words" (a leading "?" is allowed) into an object mapping each key to its decoded value, or to an array of values when the key repeats. Fails with ParseError for a bad percent-escape or a ";" separator.
url.stringifyQuery(object params) stringstringifyQuery encodes an object as a query string with sorted keys: {a: "1", b: "two words"} becomes "a=1&b=two+words". An array value repeats the key once per element; other values are converted to strings.
url.unescape(string text) result<string, error>unescape decodes a percent-encoded query component, turning "+" into a space. Fails with ParseError for a malformed escape such as "%zz".
url.withParams(string url, object params) result<string, error>withParams returns url with the given query parameters set, replacing existing values for those keys and keeping the others. An array value repeats the key. Fails with ParseError when url is malformed.