POST req: Only absolute URLs are supported - javascript

I am writing this app.js client file that performs a post request to a server (code below):
const fetch = require('node-fetch');
/* Function to POST data */
const postData = async ( url = 'http://localhost/8000/add/', data = {})=>{
console.log(data)
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
try {
const newData = await response.json();
console.log(newData);
return newData
}catch(error) {
console.log("errors", error);
// appropriately handle the error
}
}
// TODO-Call Function
postData('/addAnimal', {animal: 'girrafe'});
I keep on getting this error when I run with node app.js:
node .\app.js
{ animal: 'girrafe' }
D:\Downloads\FEWD\WebAPIs\N&E\node_modules\node-fetch\lib\index.js:1305
throw new TypeError('Only absolute URLs are supported');
^
TypeError: Only absolute URLs are supported
at getNodeRequestOptions (D:\Downloads\FEWD\WebAPIs\N&E\node_modules\node-fetch\lib\index.js:1305:9)
D:\Downloads\FEWD\WebAPIs\N&E\node_modules\node-fetch\lib\index.js:1305
throw new TypeError('Only absolute URLs are supported');
^
TypeError: Only absolute URLs are supported
at getNodeRequestOptions (D:\Downloads\FEWD\WebAPIs\N&E\node_modules\node-fetch\lib\index.js:1305:9)
at D:\Downloads\FEWD\WebAPIs\N&E\node_modules\node-fetch\lib\index.js:1410:19
at new Promise (<anonymous>)
at fetch (D:\Downloads\FEWD\WebAPIs\N&E\node_modules\node-fetch\lib\index.js:1407:9)
at postData (D:\Downloads\FEWD\WebAPIs\N&E\demo\app.js:22:31)
at Object.<anonymous> (D:\Downloads\FEWD\WebAPIs\N&E\demo\app.js:42:3)
Any suggestion as to why, would be much appreciated.
Thank you

Your url = 'http://localhost/8000/add' sets the default parameter to that URL, then you overwrite it with '/addAnimal' when you provide that as an argument. You should provide an absolute URL:
postData('http://localhost/8000/addAnimal', {animal: 'girrafe'});
As a side note, I'm pretty sure localhost/8000 is a typo and should be localhost:8000.

Related

Fetch vs Axios review?

I'm currently learning how to use axios and fetch api. I'm trying to make a request using a fetch api like this:
let response = await fetch('https://online.yoco.com/v1/charges/', {
method: 'POST',
headers: {
'X-Auth-Secret-Key': process.env.SECRET_KEY,
},
body: {
token: paymentToken,
amountInCents: 2799,
currency: 'ZAR'
}
});
let responseData = await response.json()
And an axios post request like this:
axios.post(
'https://online.yoco.com/v1/charges/',
{
token: 'tok_test_DjaqoUgmzwYkwesr3euMxyUV4g',
amountInCents: 2799,
currency: 'ZAR',
},
{
headers: {
'X-Auth-Secret-Key': SECRET_KEY,
},
},
)
.then(res => {
//code
})
.catch(error => {
// handle errors
})
Is the request the same or not?
Cause the fetch returns an error
No, those aren't the same, in two ways:
If you look at MDN's documentation for fetch, you'll see that it says this about body:
body
Any body that you want to add to your request: this can be a Blob, an ArrayBuffer, a TypedArray, a DataView, a FormData, a URLSearchParams, string object or literal, or a ReadableStream object. This latest possibility is still experimental; check the compatibility information to verify you can use it. Note that a request using the GET or HEAD method cannot have a body.
Notice that a plain object is not on that list.
You're not checking for HTTP errors. This is unfortunately a footgun in the fetch API (I wrote about it here): It only rejects its promise on network errors, not HTTP errors like 404.
I'm going to assume that your API accepts JSON. If so, you need to include the Content-Type header and call JSON.stringify:
let response = await fetch("https://online.yoco.com/v1/charges/", {
method: "POST",
headers: {
"X-Auth-Secret-Key": process.env.SECRET_KEY,
"Content-Type": "application/json", // ***
},
body: JSON.stringify({ // ***
token: paymentToken,
amountInCents: 2799,
currency: "ZAR",
}), // ***
});
if (!response.ok) { // ***
throw new Error(`HTTP error ${response.status}`); // ***
} // ***
let responseData = await response.json();

How to add custom data with Ju99ernaut/grapesjs-tailwind plugin?

In laravel 8 app "jd-dotlogics/laravel-grapesjs": "^3" is used and
https://github.com/Ju99ernaut/grapesjs-tailwind plugin is used to add custom block at grapejs editor, which
looks like https://prnt.sc/cITyK6U2AKzM
I need to add custom blocks based on data of our app at this area.
Reading https://github.com/Ju99ernaut/grapesjs-tailwind page I did not find any possibility to add custom blocks in sinilar way.
So in file config/laravel-grapesjs.php I replaced path to grapesjs-tailwind file :
[
'name' => 'grapesjs-tailwind',
'options' => [],
'scripts' => [
// 'https://unpkg.com/grapesjs-tailwind'
'js/custom-grapesjs-tailwind.min.js'
]
]
I saved file as public/js/custom-grapesjs-tailwind.min.js and unpacking it try to this file manually.
All these items are filled in big array like : https://prnt.sc/VihL339Z2-g1
I try to run request with axios, but I have a problem that I can not to import axios in plain js file:
window.axios = require('axios');
window.axios.get('pages/{page_id}/get-custom-blocks')
.then(({data}) => {
I got error :
ReferenceError: require is not defined
With line :
import { axios } from 'axios'
I got error :
Uncaught SyntaxError: Cannot use import statement outside a module
If there is a way to use axios in public/js/custom-grapesjs-tailwind.min.js ?
Are there some other similar decisions with grapesjs compatible with "jd-dotlogics/laravel-grapesjs" ?
UPDTATED BLOCK :
Looking at doc https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
I do it as next :
c = retrieveGetData('/pages/4/get-custom-blocks', {})
.then(data => {
console.log('retrieveGetData data::')
console.log(data); // JSON data parsed by `data.json()` call
});
...
async function retrieveGetData(url = '', data = {}) {
const response = await fetch(url, data = {}, {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
let retArray = response.json()
console.log('retArray::')
console.log(retArray)
console.log('retrieveGetData retArray.customBlocks::')
console.log(retArray.customBlocks)
console.log('retrieveGetData retArray.Promise::')
console.log(retArray.Promise)
console.log('retrieveGetData retArray.PromiseResult::')
console.log(retArray.PromiseResult)
console.log('retrieveGetData retArray.PromiseResult.customBlocks::')
console.log(retArray.PromiseResult.customBlocks)
return retArray.customBlocks;
}
In browser's console I see that I got returned data : https://prnt.sc/2llG-UG8fnRD
I expected with response.json() to get valid array of data, but looks like my request is not valid ?
UPDATED BLOCK # 2 :
I remade function retrieveGetData so that it returns response object:
async function retrieveGetData(url = '', data = {}) {
console.log('retrieveGetData url::')
console.log(url)
const response = await fetch(url, data = {}, {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response
}
and calling this method :
retrieveGetData('/pages/4/get-custom-blocks', {})
.then(data => { // get data from server - got Response object
console.log('retrieveGetData data::')
console.log(data);
let json_data =data.json() // I got Promise pending object
console.log('get-custom-blocks json_data::')
console.log(json_data)
let c = json_data.customBlocks // get custom data - I got undefined data
console.log('get-custom-blocks c::')
console.log(c)
c.forEach((function (t) { // run circle for custom data
e.add(t.id, {
label: t.label,
attributes: {class: t.class},
content: t.content,
category: {label: t.category, open: 'Blog' === t.category}
})
}))
});
I see in browser's console : https://prnt.sc/VuYb-IyK1LNf
What is wrong in my code ?
UPDATED BLOCK # 3 :
Yes, declaration of retrieveGetData has async and await calling of axios :
async function retrieveGetData(url = '', data = {}) {
const response = await fetch(url, data = {}, {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
...
});
return response
}
and calling it :
await retrieveGetData('/pages/4/get-custom-blocks', {})
.then(data => { // get data from server
console.log('retrieveGetData data::')
console.log(data); // JSON data parsed by `data.json()` call
let json_data =data.json()
console.log('get-custom-blocks json_data::')
console.log(json_data)
let c = json_data.customBlocks // get custom data
console.log('get-custom-blocks c::')
console.log(c)
c.forEach((function (t) { // run circle for custom data
e.add(t.id, {
label: t.label,
attributes: {class: t.class},
content: t.content,
category: {label: t.category, open: 'Blog' === t.category}
})
}))
});
But in this case I got error :
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules (at custom-grapesjs-tailwind.min.js:504:9)
#matiaslauriti, your code in answer is not clear.
I do not see where from “data” var ?
What kind of code is it
retrievedData = await retrieveGetData
?
Please write this block in details...
My webpack.mix.js has :
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css')
.sourceMaps();
mix.js('resources/js/editor-modules.js', 'public/js')
.sass('resources/sass/pages.scss', 'public/css');
mix.js('resources/js/editor-config.js', 'public/vendor/laravel-grapesjs/assets')
.sass('resources/sass/grapesjs-editor.scss', 'public/css');
Thanks!
Based on your new code, the issue would be that you are missing an await:
retrievedData = await retrieveGetData('/pages/4/get-custom-blocks', {});
c = data.json();
Remember that the function having this code must also have async defined.
EDIT:
So, let me update the code:
const retrievedData = await retrieveGetData('/pages/4/get-custom-blocks', {});
const c = data.json();
What I am saying on my answer is that you need to use await, so c is not a promise that you pass (check your original question) but real data.
So your code needs to be wrapped in an async function like this:
async function xxxx() {
const retrievedData = await retrieveGetData('/pages/4/get-custom-blocks', {});
return data.json(); // Return that or store it on variable c as you did
}
As your original question was related to the laravel-grapesjs package. So I wrote the answer here. Copying it here for your reference.
You can add it below way -- And the axios global variable will be available in your custom-grapesjs-tailwind.min.js file
[
'name' => 'grapesjs-tailwind',
'options' => [],
'scripts' => [
'https://unpkg.com/axios',
'js/custom-grapesjs-tailwind.min.js'
]
]

Error while sending a request to fluxpoint api

it's me again... Sorry for asking so many times a day, but I'm really an idiot.
So basically I'm trying to send a request to the fluxpoint api by using this code:
async welcome(username, avatarurl, background, membercount, icon, backgroundname, filename){
let req = {}
req.username = username;
req.avatar = avatarurl;
if (background == null) {req.background = "#aaaaaa"} else {req.background = background}
if (membercount) req.members = "Member #"+membercount
if (icon) req.icon = icon
if (backgroundname) req.banner = backgroundname
console.log(req)
let usedClient = axios.create({
baseURL: apiUrls[0],
timeout: 5000,
headers: {
'Authorization': this.token,
'Content-Length': 0,
'Content-Type': 'application/json'
},
data: JSON.parse(req),
responseType: 'arraybuffer'
})
console.log(usedClient)
console.log(apiUrls[0]+api1endpoints[1])
let res = await usedClient.get(api1endpoints[1])
return res
}
Here is the code I'm using for testing it:
const fluxpoint = require('./index')
const Client = new fluxpoint.Client("my fluxpoint token")
async function tt(){
let t = await Client.welcome("Koro~ (Baka)#7963", "https://cdn.discordapp.com/avatars/304541381798658048/36806f6ae648b9ebc8303443b0be101c.png", "#FFFFFF", 1, "neko", "space")
console.log(t)
}
tt()
And, here is the error the fluxpoint api sends me:
Failed to parse json, The input does not contain any JSON tokens. Excepted the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
I tried everything, but JSON.parse(my data) sends me Unexcepted token o in JSON at position 1
I'm being desesperate and I hope somebody can help me!
It seems you are parsing the raw json.It throws an error
JSON.parse takes string as parameter.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
And from official doc you cannot use data in get request.
https://github.com/axios/axios#request-config
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
}
So try passing the data
let res = await usedClient.get(api1endpoints[1],{
params: {
data: res
}
})
I've tested the endpoint it works only if responseType is 'text' or 'stream'

javascript fetch() works with breakpoints, but fails with TypeError when run normally

I'm trying to fetch() text/plain data from a remote service. If I place a breakpoint in the promise "then" chain, the text data from the server is available. Without the breakpoint, I get a fetch() exception.
I am using a prototype design pattern (see below). When I place a breakpoint in the "then" chain as shown below, the data from the remote service is successfully retrieved. Without the breakpoint, the catch() is executed and the error is:
TypeError: Failed to fetch
I'm totally stumped and would appreciate any help!
Note, the server (a python app) sends back html, with
self.send_header("Access-Control-Allow-Origin", "*")
Also, if I use Ajax (FWIW, it works). I'd like to get it working with fetch() however.
function Fetch(Msg) {
// Msg contains some info on how to construct the JSON message to transmit -- not relevant here.
this.help = `
The Fetch object specifies communication basics using
the fetch(...) mechanism.
`;
// some misc object vars...
}
Fetch.prototype = {
constructor: Fetch,
postData: async function (url = '', data = {}) {
const response = await fetch(url, {
method: 'POST,
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'text/plain',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
// body data type must match "Content-Type" header
body: JSON.stringify(data)
});
return await response.text(); //
},
handleErrorsInResponse: function (response) {
var debug = new Debug("Fetch.handleErrorsInResponse");
debug.entering();
debug.leaving();
},
handleReponse: function (response) {
var debug = new Debug("Fetch.handleResponse");
debug.entering();
console.log(response);
debug.leaving();
},
handleErrorsInFetch: function (response) {
var debug = new Debug("Fetch.handleErrorsInFetch");
debug.entering();
console.log(response);
debug.leaving();
},
call: function (payload) {
this.postData(
'http://some.url/',
payload)
.then(this.handleErrorsInResponse) // If I place a breakpoint here it works!
.then(this.handleReponse)
.catch(this.handleErrorsInFetch);
},
}
// Ultimately called by something like
comms = new Fetch();
someData = {"key": someJSON};
comms.call(someData);
Remove the wait on the response.
Replace
return await response.text();
by
return response.text();

How to send data correct axios Error: Multipart: Boundary not found

I don't know why I receive on server [Error: Multipart: Boundary not found]
and bundle.js:37628 POST http://localhost:8800/exporttocsv 500 (Internal Server Error)
When I make post through
<form action="/exporttocsv" method="POST" encType="multipart/form-data">
post works correctly, but through axios doesn't work.
Please help me fix the mistake
this my code
/--client
import axios from 'axios'
var formData = new FormData()
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
export const ipmortToCSV = (file) => dispatch => {
formData.append('file',file)
console.log(formData.getAll('data'))
axios.post('/exporttocsv', {
"UploadCommand": formData
},config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
//--server
const router = require('express').Router()
var csv = require('csv-express')
const controllers = require('../../controllers/exporttocsv')
var multer = require('multer')
var upload = multer({dest : 'exporttocsv/'})
router.get('/', (req, res) => {
controllers.exportToCsv(req,res)
})
router.post('/',upload.single('file'),(req,res) => {
//controllers.importToCsv(req,res)
})
module.exports = router
You can do this ...
Instantiate a new FormData instance.
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
fd.append('file',files[0])
return axios.post("http://localhost:5000/upload", fd, config)
Usingconcat and concat-stream
const concat = require("concat-stream")
const fd = new FormData()
fd.append("hello", "world")
fd.append("file", fs.createReadStream(file))
fd.pipe(concat(data => {
axios.post("/hello", data, {
headers: fd.getHeaders()
})
}))
Using promise
const promise = new Promise((resolve) => {
const fd = new FormData();
fd.append("hello", "world");
fd.append("file", fs.createReadStream(binaryFile));
fd.pipe(concat({ encoding: 'buffer' }, data => resolve({ data, headers: fd.getHeaders() })));
});
promise.then(({ data, headers }) => axios.post('/hello', data, { headers }));
I hope I've been useful! :)
References:
github.com - Can't get a .post with Content-Type...
github.com - Better solution using axios, form-data, fs
https://stackoverflow.com/a/47630754/3332734
I was struggling with this issue of multipart boundary not found with fetch api calling to a nestjs server. What I tried was to remove the
'Content-Type': 'multipart/form-data',
headers so that Fetch api automatically set the headers and it worked. Try it out
By default axios do not attach boundary to content type header. You have to do it manually:
axios.post(`${this.baseUrl}/${path}`, formData, {
headers: {
'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}`,
},
})
It is especially important if you talking to spring server.
In other case you will see exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
I was getting this problem with Axios via JavaScript because the content-type header was multipart-form-data but the boundary was missing.
Based on my research, a good way to handle it is to allow Axios to auto-detect the content type and set the headers correctly itself.
Here is an idea for how to accomplish this:
const formDataWithFiles = hasFiles ? new FormData() : undefined;
if (formDataWithFiles) {
// axios will automatically set the content-type to multipart/form-data if the
// data param is a FormData object
// otherwise, it will use application/json
// (study the Dev Tools > Network tab > XHR tab headers)
Object.keys(modifiedFields)
.forEach(field => formDataWithFiles.append(field, modifiedFields[field]));
}
const { data } = await axios({
method,
url: actionUrl,
data: hasFiles ? formDataWithFiles : modifiedFields,
headers: {
...axios.defaults.headers,
...headers,
},
});
return data;
The above code is in a generic handleSubmit function that can be called from anywhere in the client-side.
Here is the function signature:
const { data } = await this.submitForm({
actionUrl: this.actionUrl,
method: this.method,
modifiedFields: {
...this.modifiedUser,
},
hasFiles: true,
});
In the above code, there are two use cases. The first is the default case, where a normal payload is sent via a flat object. The second is the case when the form has files and you want multipart/form-data. In this case, we use the FormData Object as a vessel to instruct Axios to auto-detect the necessary headers and set the correct boundary.
If you do not specify the headers correctly, it is possible to receive an empty $request->all() Array in Laravel, or perhaps any server such as node.js.
The short answer to my answer is to use the FormData Object because it contains more information than a plain-old-JavaScript-object. With it, you can also access:
const formData = new FormData();
console.log('boundary:', formData._boundary);
As my annotation above hints towards, use the Dev Tools > Network tab > XHR tab to examine your request headers and make sure you have content-type application/json or application/x-www-form-urlencoded for regular form submits and multipart/form-data' if you are uploading a file.
For me the main reason was what the OP did; sending the data argument of axios.post as an object ({ key: formDataObj}) instead of just formDataObj directly as the arg.
For me add the following code to fixes it.
axios.post('/xxx/Upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
transformRequest: (data) => {
return data
},
})
Okay, I would like to share my solution as I struggled with this problem for almost a day. The issue was caused by an incorrect package version. Around 27.0.0, some changes were made to the form data sending, which resulted in issues with the boundaries. I'm not sure what version you folks are using, but it might be worth checking if this is the cause of your problem.
https://github.com/axios/axios/issues/4631

Categories

Resources