Explanation of the error
Based on the documentation of m.request()
, you can specify dynamic URLs:
Request URLs may contain interpolations:
m.request({
method: "GET",
url: "/api/v1/users/:id",
params: {id: 123},
}).then(function(user) {
console.log(user.id) // logs 123
})
In the code above, :id
is populated with the data from the params
object, and the request becomes GET /api/v1/users/123
.
Interpolations are ignored if no matching data exists in the params
property.
m.request({
method: "GET",
url: "/api/v1/users/foo:bar",
params: {id: 123},
})
In the code above, the request becomes GET /api/v1/users/foo:bar?id=123
Since your backend URL contains colons, it's interpreted as being a dynamic URL.
According to the documentation of m.buildPathname()
, m.request()
uses m.buildPathname()
internally to process dynamic URLs.
The beginning of m.buildPathname()
contains the following check regarding parameters of a path template (dynamic URL = path template populated with path parameters):
if ((/:([^\/\.-]+)(\.{3})?:/).test(template)) {
throw new SyntaxError("Template parameter names *must* be separated")
}
(Source: https://github.com/MithrilJS/mithril.js/blob/v2.0.4/mithril.js#L1288-L1292)
And, again, since your backend URL contains colons, this is where you are getting the error. (You can verify this by trying to run m.buildPathname('http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend')
– you'll get the same error.)
How to fix it
Since you can't get away from that regex check at the beginning of m.buildPathname()
, your best bet might be to use a dynamic URL. Like so:
m.buildPathname(':url...', { url: 'http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend' })
// => http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend
Or when applied to your code:
async post(url, body = {}) {
return new Promise((resolve, reject) => {
m.request({method: 'POST', url: ':url...', body, params: {url}}).then((data) => {
resolve(data);
}).catch((err) => {
reject(err.message);
});
});
}
Or alternatively you can specify the (dynamic) URL as the first argument of m.request()
:
async post(url, body = {}) {
return new Promise((resolve, reject) => {
m.request(':url...', {method: 'POST', body, params: {url}}).then((data) => {
resolve(data);
}).catch((err) => {
reject(err.message);
});
});
}
Notice that there are three dots after the path parameter :url
. Otherwise its value would be escaped/encoded. This is mentioned in the documentation of path handling. Example:
m.buildPathname(':url', { url: 'http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend' })
// => http%3A%2F%2F%5B340f%3Ac0e0%3A1d1%3A5gc0%3Ag4fs%3A2%3A%3A%5D%3A22923%2Fbackend
Handling URL parameters
As mentioned in the other answer, if the URL contains parameters, the question mark will be duplicated:
m.buildPathname(':url...', { url: 'https://example.com/foo?bar=baz' })
// => https://example.com/foo??bar=baz
// ^^
One way to solve that would be to include the parameters in the path template:
const url = 'https://example.com/foo?bar=baz'
const [baseUrl, params] = url.split('?')
const template = ':baseUrl...' + (params ? `?${params}` : '')
m.buildPathname(template, { baseUrl })
// => https://example.com/foo?bar=baz
However, if there are colons in the URL parameters, there's a possibility that you'll get the same error as originally ("Template parameter names *must* be separated").
There might be a way to solve this, but the previous code sample is already quite complex for this relatively simple use case. Which leads us to:
Alternative solution: don't use m.request()
m.request()
is just "a thin wrapper around XMLHttpRequest
." It "returns a promise and triggers a redraw upon completion of its promise chain."
If m.request()
is difficult to work with due to using IPv6 URLs (or for other reasons), it can be easier to use something else for doing XHR requests. You could for example use fetch()
– just remember to call m.redraw()
at the end (m.request()
does this automatically).
Sure, m.request()
does more than just calls m.redraw()
at the end (see the docs), but it's also okay to use something else.