I am having trouble adding a header to a request-promise (npm) request using a variable (headersTest). If I hard-code the value in options, I receive the expected API response. If I create the header in a variable and add that to options, the API rejects the request (the header key is not recognised). When I log the headersTest variable it would appear identical to the hard-coded value, but doesn't work.
Here is the relevant code section:
const headersTest = "'x-custom-date':'Fri, 27 Oct 2017 09:45:18 EST'";
console.log(headersTest);
const options = {
method: "GET",
uri: 'http://' + whatEvz,
qs: {
queryString
},
headers: {
//'x-custom-date':'Fri, 27 Oct 2017 09:45:18 EST'
headersTest
},
json: true
};
rp(options)
.then(function(results){
res.send(results);
})
.catch(function (err) {
res.send("Shite! That didn't work! " + err.message);
});
It seems I'm missing something in my understanding as to how variables are interpreted within options. Any guidance would be much appreciated.
I solved this by using object properties, rather than trying to pass on object into options.headers. I actually had an array of headers and used lodash to add the headers like so:
_.each(myHeaders, function (val, key) {
_.each(val, function (val, key) {
options.headers[key] = val;
});
})
Related
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.
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.
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.
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.
I am having a problem using Axios with node js. Here is my code
let callResult = await axios.get(urlData, config)
Where config is
let config = {
headers: {
'X-Token': token
}
};
And the urlData is
let urlData = 'https://api.regulaforensics.com/webapi/Transaction2/GetTransactionResultJson?transactionId=<IDVariable>&resultType=15'
I am trying to add my IDVariable to the URL but it does not work. If I take the variable and put it directly in the URL I get a response.
I have also tried this
let config = {
headers: {
'X-Token': token
},
params: {
transactionId: IDVariable,
resultType: 15
}};
And this
let querys = querystring.stringify({ transactionId: keyId, resultType: 15 })
let path = 'https://api.regulaforensics.com/webapi/Transaction2/GetTransactionResultJson?'
let urlData = path.concat("", querys)
This is the complete URL
https://api.regulaforensics.com/webapi/Transaction2/GetTransactionResultJson?transactionId=05cc6ccc-3ae6-4185-b2c9-1e1aba01d705&resultType=15
When using {params: } or concatenation
When putting the whole URL. As the URL I pasted above
This is my whole function
If you want to pass some parameters as a query string you can use the following syntax for the GET request:
axios.get('/user', { params: { ID: 12345 } });
Tath will be translated in the following request:
/user?ID=12345
As explained in the documentation here: https://github.com/axios/axios#note-commonjs-usage
Note
You don't need to add the ? char at the end of URL and you don't need to have the parameters in the URL, so the part ?transactionId=<IDVariable>&resultType=15 must be removed
I found the solution.
I am calling a service first and the response of that service is the key I need for the second service.
The solution was to just put a sleep() between those two services.
Thanks you for your response, guys!
I am using RestAPI from Wikibase which in turn use Hyperswitch framework.
In the example code of its yaml file, there is a case to choose to return the response or not depend on the status code
x-request-handler:
- storage:
request:
method: get
headers:
cache-control: '{{cache-control}}'
# cache-control: "no-cache"
uri: /{domain}/sys/key_value/page_summary3/{request.params.title}
catch:
status: 404
return_if:
# Typical case: Return straight from storage.
status: '2xx'
return:
But I would like to return depend on a value found on the response body, say success: 1. How can I do that?
return_if:
'{{storage.body.success}}': 1
maybe? That syntax works in values, I don't know if it works in keys. You can always handle the request manually though: use operationId instead of x-request-handler, and then have a function like
function storage(hyper, req) {
return hyper.get(...).then((res) => {
if (res.body.success === 1) {
return res;
}
return {...};
});
}
and export it with the operation ID you chose.
I'm trying the Wikipedia client login flow depicted in the API:Login docs, but something wrong happens:
1) I correctly get a token raised with the HTTP GET https://en.wikipedia.org/w/api.php?action=query&meta=tokens&type=login&format=json
and I get a valid logintoken string.
2.1) I then try the clientlogin like:
HTTP POST /w/api.php?action=clientlogin&format=json&lgname=xxxx&lgtoken=xxxx%2B%5C
and the POST BODY was
{
"lgpassword" : "xxxxx",
"lgtoken" : "xxxxx"
}
But I get an error:
{
"error": {
"code": "notoken",
"info": "The \"token\" parameter must be set."
},
"servedby": "mw1228"
}
If I try to change lgtoken to token I get the same result.
2.2) I have then tried the old method i.e. action=login and passing the body, but it does not work, since it gives me back another login token: HTTP POST https://en.wikipedia.org/w/api.php?action=login&format=json&lgname=xxxx
and the same POST BODY
I then get
{
"warnings": {}
},
"login": {
"result": "NeedToken",
"token": "xxxxx+\\"
}
where the docs here states that
NeedToken if the lgtoken parameter was not provided or no session was active (e.g. your cookie handling is broken).
but I have passed the lgtoken in the json body as showed.
I'm using Node.js and the built-in http module, that is supposed to pass and keep session Cookies in the right way (with other api it works ok).
I have found a similar issue on a the LrMediaWiki client here.
[UPDATE]
This is my current implementation:
Wikipedia.prototype.loginUser = function (username, password) {
var self = this;
return new Promise((resolve, reject) => {
var cookies = self.cookies({});
var headers = {
'Cookie': cookies.join(';'),
'Accept': '*/*',
'User-Agent': self.browser.userAgent()
};
// fetch login token
self.api.RequestGetP('/w/api.php', headers, {
action: 'query',
meta: 'tokens',
type: 'login',
format: 'json'
})
.then(response => { // success
if (response.query && response.query.tokens && response.query.tokens['logintoken']) {
self.login.logintoken = response.query.tokens['logintoken'];
self.logger.info("Wikipedia.login token:%s", self.login);
return self.api.RequestPostP('/w/api.php', headers, {
action: 'login',
format: 'json',
lgname: username
},
{
lgpassword: password,
lgtoken: self.login.logintoken
});
} else {
var error = new Error('no logintoken');
return reject(error);
}
})
.then(response => { // success
return resolve(response);
})
.catch(error => { // error
self.logger.error("Wikipedia.login error%s\n%#", error.message, error.stack);
return reject(error);
});
});
}//loginUser
where this.api is a simple wrapper of the Node.js http, the source code is available here and the api signatures are like:
Promise:API.RequestGetP(url,headers,querystring)
Promise:API.RequestPostP(url,headers,querystring,body)
If the currently accepted answer isn't working for someone, the following method will definitely work. I've used the axios library to send requests. Any library can be used but the key lies in formatting the body and headers correctly.
let url = "https://test.wikipedia.org/w/api.php";
let params = {
action: "query",
meta: "tokens",
type: "login",
format: "json"
};
axios.get(url, { params: params }).then(resp => {
let loginToken = resp.data.query.tokens.logintoken
let cookie = resp.headers["set-cookie"].join(';');
let body = {
action: 'login',
lgname: 'user_name',
lgpassword: 'password',
lgtoken: loginToken,
format: 'json'
}
let bodyData = new URLSearchParams(body).toString();
axios.post(url, bodyData, {
headers: {
Cookie: cookie,
}
}).then(resp => {
// You're now logged in!
// You'll have to add the following cookie in the headers again for any further requests that you might make
let cookie = resp.headers["set-cookie"].join(';')
console.log(resp.data)
})
})
And you should be seeing a response like
{
login: { result: 'Success', lguserid: 0000000, lgusername: 'Username' }
}
The second post request was where I got stuck for several hours, trying to figure out what was wrong. You need to send the data in an encoded form by using an API like URLSearchParams, or by just typing up the body as a string manually yourself.
I think from what you are saying you have lgtoken and lgname in the URL you are using, and then lgpassword and lgtoken (again!) in a JSON-encoded POST body.
This is not how the Mediawiki API works.
You submit it all as POST parameters. JSON is never involved, except when you ask for the result to come back in that format. I can't help you fix your code as you don't provide it, but that's what you need to do. (If you edit your question with your code, I'll do my best to help you.)
After seeing your code, I'll presume (without knowing the detail of your code) that you want something like this:
return self.api.RequestPostP('/w/api.php', headers, {
action: 'login',
format: 'json',
lgname: username,
lgpassword: password,
lgtoken: self.login.logintoken
});
I am just calling the api get using reactJS and axios.
The api call is going but I'm unable to get the response from the server.
Here is my code:
getProjectdetails(details) {
alert("coming")
details.map(function(project, i) {
var projectId = project.projectId
axios.get('http://workpresso.rankworld.io/api/v1/project?query={"projectId":"' + projectId + '"}', {
params: {},
headers: {
"x-access-token": sessionStorage.token
},
})
.then(function(response1) {}.bind(this))
.catch(function(error) {
console.log(error);
});
})
}
You'll need to return something from the then().
If you want to avoid binding 'this' you can use an arrow function (if es6 is available, assuming you're using Node, it probably is).
Lastly, a map function transforms the array elements, so you need to return something in that function too.