Error `Template parameter names *must* be separated`

299 Views Asked by At

I am using mithril.js in frontend application and backend application is running on ipv6 environment.

Calling post ajax request to backend using mithril.js.

async post(url, body = {}) {
        return new Promise((resolve, reject) => {
            m.request({method: 'POST', url, body}).then((data) => {
                resolve(data);
            }).catch((err) => {
                reject(err.message);
            });
        });
}

Backed url is like this: http://[340f:c0e0:1d1:5gc0:g4fs:2::]:22923/backend.

But getting this error Template parameter names *must* be separated while calling backend api.

2

There are 2 best solutions below

0
On

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.

1
On

Thanks mts knn for the reply. We have implemented your solution however we faced below issues. Question mark is passing two times in http url of api. Please find attached below screenshot. enter image description here

In order to fix this problem, please find the updated code below

async post(url, body = {}) {
    var queryIndex = url.indexOf('?');
    var httpPart = url.slice(0,queryIndex);
    var finalUrl = url.replace(httpPart,":url..."); 
    return new Promise((resolve, reject) => {
       m.request({method: 'POST', url: finalUrl, body, params: {url: httpPart}}).then((data) => {
                resolve(data);
            }).catch((err) => {
                reject(err.message);
            });
        });
 }

You can also provide efficient solution If any.