How to send requests using custom cookies with Request-promise - node.js

I am working on a print app and I need to get some data from a protected resources.
the only way I can be signed in is using a browser so I am using phantom as browser.
I manged to sign in and get the cookies but I don't know how to use the cookies with request-promise to send the requests with the cookies I got when I signed in.
Here is my simplified code:
import * as phantom from "phantom";
import * as requestPromise from "request-promise-native";
requestPromise.defaults({ rejectUnauthorized: false });
(async function () {
const instance = await phantom.create([
"--ignore-ssl-errors=true",
"--cookies-file=cookies.txt",
"--web-security=false",
"--ssl-protocol=any"
]);
const page = await instance.createPage();
const status = await page.open("http://localhost:3000/auth/login-html");
console.log("status", status);
let result = await page.evaluate(() => {
(document.getElementById("username") as HTMLInputElement).value = "test#email.com";
(document.getElementById("password") as HTMLInputElement).value="password";
(document.getElementById("login") as HTMLElement).click();
return true;
});
// Get the cookies
let cookies = await page.property("cookies");
console.log(cookies)
/** [{ domain: 'my-app-resource-domain.com',
httponly: true,
name: 'ticket',
path: '/',
secure: false,
value: '6823a-78c0a4ee82c0' },
{ domain: 'my-app-resource-domain.com',
httponly: true,
name: 'JSESSIONID',
path: '/',
secure: false,
value: '9CB8396A05ABA178' }] **/
let cookiesSring = getStringCookies(cookies) // "ticket=6823a-78c0a4ee82c0&JSESSIONID=9CB8396A05ABA178"
requestPromise.cookie(cookiesSring );
// Send request to get data from protected resource
let res = await requestPromise.get("http://my-app-resource-domain.com/api/get-charts")
})();

The documentation is very unclear about it, but the cookie function actually only just parses and returns a cookie string as an object. It does not set the cookie to be sent in the request.
You have two options. Either you use tough-cookie to create a cookie jar and include it in the options, as shown in the official documentation, or you simply include your cookies in the header directly.
Using a cookie jar
import * as requestPromise from "request-promise-native";
import { Cookie } from "tough-cookie";
// ...
const pageCookies = await page.property("cookies");
// rename the "httponly" property to "httpOnly" since that's what
// tough-cookie expects
const cookies = pageCookies.map(pageCookie => {
const cookie = { ...pageCookie };
cookie.httpOnly = pageCookie.httponly;
delete cookie.httpOnly;
return new Cookie(cookie);
});
const cookieJar = requestPromise.jar();
// Set the cookies to apply only to my-app-resource-domain.com
cookies.forEach(cookie =>
cookieJar.setCookie(cookie, "http://my-app-resource-domain.com/")
);
const options = {
uri: "http://my-app-resource-domain.com/api/get-charts",
jar: cookieJar
};
const res = await requestPromise.get(options);
Using headers
import * as requestPromise from "request-promise-native";
import * as querystring from "querystring";
// ...
const pageCookies = await page.property("cookies");
// pluck the name and value from the page cookies into
// key-value pairs in an object
const cookieObjects = pageCookies.reduce((acc, cookie) => {
acc[cookie.name] = cookie.value;
return acc;
}, {});
// stringify the cookies to the familiar "cookie=value; cookie2=value" format
const cookieString = querystring.stringify(cookieObjects, "; ");
const options = {
uri: "http://my-app-resource-domain.com/api/get-charts",
headers: {
Cookie: `${cookieString};`
}
};
const res = await requestPromise.get(options);

Related

How can i get a client side cookie with next.js 13 before rendering from middleware

I am trying to get a cookie from the middleware of Nextjs 13 and I can't find up to date information on this.
My question: is there a way to see the cookie before rendering the html.
window is fined or something?
page.tsx
async function getData() {
const nextCookies = cookies(); // Get cookies object
try {
let cityToken = nextCookies.get('regionCode'); // Find cookie city
return cityToken;
} catch (e) {
console.log(e);
}
}
export default async function Page() {
const nextCookies = cookies(); // Get cookies object
const token = nextCookies.get('new'); // Find cookie for is new user
// const cityToken = await getData();
const cityToken = cookies().get('regionCode')?.value;
console.log(cityToken);
}
middleware.tsx
const myFunction = async () => {
// https://geolocation.onetrust.com/cookieconsentpub/v1/geo/location
// run asynchronous tasks here - ip location
const response = await fetch('https://www.cloudflare.com/cdn-cgi/trace');
const text = await response.text();
const arr = text.split('\n');
const ip = arr.filter((v) => v.includes('ip='))[0].split('=')[1];
// https://stackoverflow.com/questions/65752416/how-can-i-most-easily-parse-this-http-response-into-json-to-then-access-response
// https://stackoverflow.com/questions/64591296/access-to-fetch-at-https-www-cloudflare-com-cdn-cgi-trace-from-origin-http
const ipCity = await fetch(`https://ipapi.co/${ip}/json/`);
const textJson = await ipCity.json();
};
const data = await myFunction();
res.cookies.set('regionCode', 'textJson.region');

Cannot GET /[object%20Object] when calling axios.get()

When I paste the endpoint URL with query directly inside the axios.get(), it responds correctly and I can see the json object returned. (i.e axios.get(http://localhost:3000/api/products/product_search?secretKey=${secret}&id=${blabla})). However, if I call the url with the summonerByNameUrl method, it crashes when I make a request. What is the problem in my code?
Crash report:
...
data: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>Cannot GET /[object%20Object]</pre>\n' +
'</body>\n' +
'</html>\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Code:
config.js
const summonerByNameUrl = (summonerName) => `${URL(hidden)}${summonerName}`;
module.exports = {
summonerByNameUrl
}
summoner.js
const config = require('../config');
const axios = require('axios');
const getSummonerByName = async (summonerName) => {
const res = await axios.get(config.summonerByNameUrl(summonerName));
return res.data;
}
const summonerParser = async (req, res) => {
if(!req.query.secretKey)
return res.status(403).json({error: 'missing secret key.'})
let data = await getSummonerByName(req.query)
return res.status(200).json(data);
}
module.exports = {
getSummonerByName,
summonerParser
}
products.js
var express = require('express');
var axios = require('axios')
var router = express.Router();
const summoner = require('../services/summoner');
router.get('/product_search', summoner.summonerParser)
module.exports = router;
app.js
...
app.use('/api/products', productsRouter);
...
You're calling your function with getSummonerByName(req.query) where it is clear from the lines just before that req.query is an object and not a string. When objects are used in a string-context (like your URL), they become "[object Object]", hence the error.
Taking some guesses here but it seems you want to forward some req.query information to the Axios call as query params. Try this instead...
const PRODUCT_SEARCH_URL = "http://localhost:3000/api/products/product_search"
const getSummonerByName = async ({ secretKey, id }) => {
const { data } = await axios.get(PRODUCT_SEARCH_URL, {
params: { secretKey, id }
})
return data
}
If you've got a helper function that returns the base URL (ie http://localhost:3000/api/products/product_search) then by all means, use that instead of a string literal in the Axios call.
The req.query is a Object, not a string.
You can try map the req.query object to make a string. Something like that:
Object.keys(req.query).map(key => {
return key + '=' + req.query[key]
}).join('&')
This code return a string like that: 'id=1&name=test', so you can pass to the endpoint.

How to add data via post in a request in Puppeteer?

I am trying to scrape a webpage with Puppeteer. Enter, navigate through some pages and in the data pages (those that are paginated) add POST data (emulating the form).
The event to intercept the request can only be created once, so all calls will be affected by the data sent via POST. (Node Puppeteer, page.on( "request" ) throw a "Request is already handled!")
I didn't find much information on this (how do POST request in puppeteer?), and finally did the following:
Create a function that will always be called (on each request).
Query an attribute of the function to see if it has an object.
If you have it, embed the data via POST; and remove the attribute.
If the attribute does not exist, continue without embedding data.
const openConnection = async () => {
const browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox"],
});
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on("request", requestPost);
return { browser, page };
};
const requestPost = async (req) => {
if (typeof requestPost.data === "object") {
requestPost.data.headers = { ...req.headers(), ...requestPost.data.headers };
await req.continue(requestPost.data);
delete requestPost.data;
} else {
await req.continue();
}
};
const getData = async (m, y, p, l) => {
const { browser, page } = await openConnection();
let data = [];
let pagina = p;
do {
/* JUST because this attribute is being created, the next request that is created in the page.goto() that follows, will be altered with these attributes */
requestPost.data = {
method: "POST",
postData: `&pagina=${pagina}&mes=${m}&year=${y}`,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
};
await page.goto("https://url.com/info.cgi", { waitUntil: "networkidle2" });
// Now I work the data and add it to the end
// data = data.push();
pagina++;
} while (pagina < p + l);
await closeConnection(page, browser);
return data;
};

Dialogflow webhook fulfillment parameter is not working in post API

I make a code in webhook were i want invoke POST API and i want to invoke that api for that i have to pass some parameter but whenever i am trying to pass parameter coming from dialogflow its gives error. My code is like that
//Self Hosted Express Server
const bodyParser = require('body-parser')
var request = require('request-promise-native');
const { dialogflow } = require('actions-on-google');
const assistant = dialogflow({
clientId: "305xxxxxx7-rv9kocdq2xxxxouuq8f9ul2eg.apps.googleusercontent.com"
});
module.exports = (app) => {
const logger = console;
assistant.intent('Sales',(conv, params) => {
var pcode = params.myproduct;
// console.log(pcode)
const token = '3369708919812376';
const serviceID = '502';
const P_STATE_CD = 'ALL';
const P_FO_CD = 'ALL';
const P_DISTT_CD = 'ALL';
const P_DATE = '16/12/2019';
const P_PRD_GROUP = pcode;
const P_PERSONAL_NO = '106296';
var data = {"token" : token,"serviceID" : serviceID,"P_STATE_CD" : P_STATE_CD,"P_FO_CD" : P_FO_CD,"P_DISTT_CD" : P_DISTT_CD,"P_DATE" : P_DATE,"P_PRD_GROUP" : P_PRD_GROUP ,"P_PERSONAL_NO" : P_PERSONAL_NO };
var sdata = JSON.stringify(data);
const options = {
method: 'POST',
uri: 'http://chatbotWebservice/resources/webservice/service' ,
body: JSON.parse(sdata) ,
json: true
}
return request(options)
.then( body => {
var unit = body
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
unit.value2 = unit.saleInfo[0].QYTD
unit.value3 = unit.saleInfo[0].O_UOM
unit.value4 = null
unit.value5 = null
delete unit.saleInfo
var unit2 = JSON.stringify(unit)
console.log(unit2)
conv.ask(unit2);
})
.catch( err => {
console.error( err );
conv.ask('Something went wrong. What should I do now?');
});
})
And the error like this
TypeError: Cannot read property '0' of undefined
at request.then.body (/home/dbalounge/GoogleDF/service.js:40:44)
at process._tickCallback (internal/process/next_tick.js:68:7)
Please help me out this. Thank You in Advance
Apparently body is coming as a string, probably because the server is not setting the correct Content-Type to the response, and request is ignoring json: true option. So you should use JSON.parse on it, and then access the saleInfo
return request(options)
.then( body => {
var unit = JSON.parse(body)
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
/* ... */
});
Aside from that body: JSON.parse(sdata) in your call is not needed, you're stringifying data to sdata to parse it back again, just use data directly:
const options = {
method: 'POST',
uri: 'http://chatbotWebservice/resources/webservice/service' ,
body: data,
json: true
}

Request body is empty when posting form-data

I'm using a simple post request to my backend for a form data and for some reason the body is alwayes empty.
I'm trying to isolate this so i changed the content type to application json and changed the data to json and only this way i can send data.
Client side:
submitForm(event) {
event.preventDefault();
console.log("gggg");
const data = new FormData(event.target);
axios.post("http://localhost:4000/user-form-post",data).then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
Server side:
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
app.use(express.urlencoded());
// Parse JSON bodies (as sent by API clients)
app.use(express.json());
app.use(logger('dev'));
app.post('/user-form-post', (req,res) =>{
console.log("dfdf");
console.log(req.body); // alwayes print empty dict {}
res.end();
})
This is not working because it expects jsons(expected behavior):
// app.use(bodyParser.json());
// app.use(bodyParser.urlencoded({extended:true}));
Same behavior with Postman.
You will need to parse your form data from express side. For this you will have to use multer or multiparty. Try something like this. refer the documentation as well
const multiparty = require('multiparty');
app.post('/user-form-post', (req,res) =>{
let form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
Object.keys(fields).forEach(function(name) {
console.log('got field named ' + name);
});
});
})
when it comes to my issue,
i have this front end
const form = new FormData();
form.email = this.email;
form.password = this.password;
console.log("onSubmit -> form", form);
axios.post("http://localhost:3000/register", form )
onSubmit -> form FormData {email: "admin#gmail.com", password: "123"}
but the req.body in backend is empty, and i figured it out that the form in axios.post still need 1 more bracket {} even it's a object. like this
axios.post("http://localhost:3000/register", { form })
After that backend got body like this
req.body = { form: { email: 'admin#gmail.com', password: '123' } }
A problem with request body when you post data is data type .
I have recently a problem with Postman .
You should post data with type x-www-form-urlencoded or raw->JSON to fix the problem.
Goodluck.
You are using:
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
Please, also use given below line code but first install multer and write the code in top of your application:
var multer = require('multer');
var upload = multer();
app.use(express.json());
Faced the same issue , spent 2 days . Here are the solutions i found :
my request payload had JSON.stringify() , it will make body as {} empty object . when i removed JSON.stringify() and sent request it worked .
Content type should be multipart-form :boundary -----
Now if i externally set it to multipart-form , boundary thing was missing.
for few people it worked when you set content-type as false / undefined , boundary thing got added up,but not for me .
Even though i followed all steps and sending FormData as payload, payload was request payload object in network tab and was not FormData object , my request failed with 500 .
i tried the below code , its react + typescript (make necessary changes to avoid syntax errors)
import QueryString from 'qs';
import { ApiParams } from './xyzfile';
import { ApiHandlerRawType } from './types/xyzfile';
const setDefaultOptions = ({
method = '',
url = '',
params = {},
data = {},
signal = null,
headers = new Headers(),
...options
} = {}) => ({
method,
url,
params,
signal,
headers,
data,
...options
});
const setData = ({ method, data, ...options }: ApiHandlerRawType) => {
const option = options;
if (method !== 'GET' && option.isStreamData) {
option.body = data;
}
return {
method,
...option
};
};
const addRequestHeaders = ({ headers = new Headers(), ...options }) => {
const { existingHeaders }: ApiHandlerRawType = options;
if (existingHeaders) {
Object.entries(existingHeaders).forEach(([key, value]) => {
if (key !== 'Content-Type') headers.set(key, value);
});
}
return {
headers,
...options
};
};
export const ApiHandlerRaw = ({
url,
...originalOptions
}: ApiHandlerRawType): Promise<Response> => {
const options = setData(
addRequestHeaders(setDefaultOptions(originalOptions))
);
return fetch(url || '', options)
.then(response => {
if (!response.ok) throw new Error(response.statusText);
return Promise.resolve(response);
})
.catch(err => Promise.reject(err));
};
export const FileUploadApiHandler = async ({
headers,
...options
}: ApiHandlerRawType): Promise<Response | Blob> => {
const response = await ApiHandlerRaw({
headers,
isStreamData: true,
...options
});
return response;
};
export const fileApiService = ({
url,
method,
qsObject,
headers,
reqObjectAsStreamData
}: ApiParams): Promise<Response> => {
const qs = QueryString.stringify(qsObject, { addQueryPrefix: true });
const urlPath = `${url}${qs}`;
const data = reqObjectAsStreamData;
const existingHeaders = headers;
return FileUploadApiHandler({
url: urlPath,
method,
data,
existingHeaders
}) as Promise<Response>;
};
send the required variables from fileApiService . existingHeaders would be your app headers , eg : token / ids ... etc . data in fileApiService is the body .
I have also faced the same issue in the published code.
But I have fixed this issue by using the below code highlighted in the attached image :-
enter image description here
There is no use of "Content-Type" to fix this issue.
Hope you fix your issue by using the above code snippets.

Resources