I am having an API Service build on node js running on port 3001 and a web UI using REACT & running on port 3000. I have to POST details from Web page to the API in JSON Format. I am able to hit the API, however I could not get the JSON on the API. Its being received as {}. However the API is working fine from postman.
Could some one please share light on where i am missing.
Source code of API
const util = require('util')
const request = require("request");
const express = require('express')
const app = express();
const port = 3001
app.use(express.json());
app.use(express.urlencoded( {extended: false}));
const bodyParser = require('body-parser');
const { json } = require('express/lib/response');
app.post('/PostPatronDetailsone',(req,res) => {
async function run() {
try {
console.log('Request received from REACT ONE');
// parse application/json
app.use(bodyParser.json())
console.log(req.body);
// Respond back
res.send('Transaction Sent');
} finally {
// Ensures that the client will close when you finish/error
}
}
run().catch(console.dir);
});
app.listen(port, () => console.log('Server is listening at port ' + port));
Code Snippet of Web making the HTTP POST
import React from 'react';
class Signup extends React.Component {
constructor(props) {
super(props);
this.state = {
postId: null
};
}
async componentDidMount() {
// Simple POST request with a JSON body using fetch
let payload = {'first_name': 'TEST' };
var data = new FormData();
data.append( "json", JSON.stringify( payload ) );
const requestOptions = {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json','Accept':'application/json' },
body: data,
redirect: 'follow'
};
var response = await fetch('http://localhost:3001/PostPatronDetailsone', requestOptions);
var data1 = await response.json();
var data2 = data1.text();
alert(data2);
//this.setState({ postId: data1.insertedid });
}
render() {
const { postId } = this.state;
return (
<div className="card text-center m-3">
<h5 className="card-header">Simple POST Request</h5>
</div>
);
}
}
export default Signup;
Console Output of API Service
Server is listening at port 3001
Request received from REACT ONE
{}
Request received from REACT ONE
{}
It is because you send formData, instead stringify the object and send it to body like this:
async componentDidMount() {
// Simple POST request with a JSON body using fetch
let payload = {first_name: 'TEST' };
const requestOptions = {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json','Accept':'application/json' },
body: JSON.stringify(payload ),
redirect: 'follow'
};
var response = await fetch('http://localhost:3001/PostPatronDetailsone', requestOptions);
var data1 = await response.json();
var data2 = data1.text();
alert(data2);
//this.setState({ postId: data1.insertedid });
}
Please change the code as follows:
let payload = {'first_name': 'TEST' };
fetch('http://localhost:3001/PostPatronDetailsone',{
body:JSON.stringify(payload),
method:'post',
mode: 'no-cors',
headers: {
"Content-Type": "application/json"
},
}).then((res)=>res.json()).then((data)=>console.log(data));
And from controller in backend remove that line as you are already using express.json() which is equivalent to that line:
app.use(bodyParser.json()) // remove it
You have 3 key problems here:
You are posting a FormData object (which gets converted to a multipart/form-data encoding) and not JSON
You are setting no-cors mode which causes the browser to silently discard any instructions to do anything that would require CORS permission (such as setting the application/json content-type or reading the response)
There won't be a text() method on anything parsed from JSON
const data = {'first_name': 'TEST' };
const payload = JSON.stringify(data)
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json','Accept':'application/json' },
body: payload,
redirect: 'follow'
};
const response = await fetch('http://localhost:3001/PostPatronDetailsone', requestOptions);
const parsed_json = await response.json();
console.log(parsed_json);
You will also need to change the server-side code to grant permission to the JavaScript to read the response and send the JSON formatted request. Use the cors module for that.
app.use(cors());
Also note that body parsing middleware needs to be set up when the application starts and not in the middle of a route. The body-parser module has been obsoleted as Express has its features built-in now.
The following is good:
app.use(express.json());
The following is bad:
const bodyParser = require('body-parser');
and
app.use(bodyParser.json())
Your route code doesn't need to be any more complex than:
app.post('/PostPatronDetailsone', async (req,res) => {
console.log('Request received from REACT ONE');
console.log(req.body);
res.json('Transaction Sent');
});
Note that since you are trying to parse the response as JSON in the client side code, I've had to change send to json so that the server outputs JSON instead of plain text.
Related
I am using React + NodeJS & Axios but have been trying to send a post request but experiencing difficulties.
The request seems to be posting successfully, but all actions at the nodejs server is returning in the "undefined" data value, even if the data is passed successfully shown in the console.
REACT
const fireAction = (data1, data2) => {
const data = JSON.stringify({data1, data2})
const url = `http://localhost:5000/data/corr/fire`;
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'AUTHCODE',
}
}
axios.post(url, data, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
fireAction("Oklahoma", "Small apartment")
NODE
app.post('/data/corr/fire', async (req, res) => {
try {
const data = req.body.data1;
console.log(data)
} catch(e) {
res.send({success: "none", error: e.message})
}
});
Result of node: "undefined"
I have added the following body parser:
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
I am not sure why this error is happening. I see there is similar questions to mine: however none of them are applicable as I'm using both express and body parser which is already suggested.
You're POSTing JSON with a content-type meant for forms. There's no need to manually set content-type if you're sending JSON, but if you want to manually override it, you can use 'Content-Type': 'application/json', and access the response in your route with req.body. If it does need to be formencoded, you'll need to build the form:
const params = new URLSearchParams();
params.append('data1', data1);
params.append('data2', data2);
axios.post(url, params, config);
I can do a put request just fine in Postman.
But when I try to do the same put request in a deno fresh app through a fetch command like this:
async function sendSignature() {
const signer = provider.getSigner();
const nonce = "asdf123492fd";
const signatureSigned = await signer.signMessage(nonce);
const headers = new Headers({
'Content-Type': 'application/json'
});
const opts = {
method: 'PUT',
headers: headers,
body: JSON.stringify({
key: props.singleUser,
wallet_address: walletAddrs,
signature: signatureSigned
})
}
console.log(opts.body);
const rawPosts = await fetch('http://localhost:4000/users/kythis1', opts);
console.log(rawPosts);
}
Btw all of the values are being populated in body. I can confirm that key, wallet_address, and signature are not null and are strings. It fails though... Here's what the browser's console looks like.
This is the entry point for the backend oak (deno's version of express) server.
import { Application } from "https://deno.land/x/oak/mod.ts";
import { APP_HOST, APP_PORT } from "./config.js";
import router from "./routes.js";
import _404 from "./controllers/404.js";
import errorHandler from "./controllers/errorHandler.js";
import { oakCors } from "https://deno.land/x/cors/mod.ts";
const app = new Application();
app.use(oakCors()); // Enable CORS for All Routes
app.use(errorHandler);
app.use(router.routes());
app.use(router.allowedMethods());
app.use(_404);
console.log(`Listening on port:${APP_PORT}...`);
await app.listen(`${APP_HOST}:${APP_PORT}`);
This is the function that is getting called by the put request:
import User from "../db/database.js";
export default async ({ request, response }) => {
if (!request.hasBody) {
response.status = 400;
response.body = { msg: "Invalid user data" };
return;
}
const body = request.body();
const {
key, wallet_address, signature
} = JSON.parse(await body.value);
console.log(signature);
if (!key) {
response.status = 422;
response.body = { msg: "Incorrect user data. Email and key are required" };
return;
}
const foundUser = await User.where('key', '=', key).first();
if (!foundUser) {
response.status = 404;
response.body = { msg: `User with key ${key} not found` };
return;
}
foundUser.wallet_address = wallet_address;
foundUser.updated_at = new Date();
const updatedResp = await foundUser.update();
response.body = { msg: "User updated", updatedResp };
};
Finally this is the backend routes:
import { Router } from "https://deno.land/x/oak/mod.ts";
import getUsers from "./controllers/getUsers.js";
import getUserDetails from "./controllers/getUserDetails.js";
import createUser from "./controllers/createUser.js";
import updateUser from "./controllers/updateUser.js";
//import deleteUser from "./controllers/deleteUser.js";
const router = new Router();
router
.get("/users", getUsers)
.get("/users/:key", getUserDetails)
.post("/users", createUser)
.put("/users/:key", updateUser);
//.delete("/users/:id", deleteUser);
export default router;
So why can I successfully call this function with a Postman put request, but I can't do a successful put request with fetch through a typescript file?
Your postman and fetch call aren't exactly the same.
Looking at postman it has 8 headers, and content-type seems to be set on Text.
While the fetch() is set on application/json.
When content type is application/json your request.body() is likely already parsed. Thus another JSON.parse will throw an error.
One easy fix with your current code would be to set your javascript headers to this:
const headers = new Headers({
'Content-Type': 'text/plain'
});
This will avoid 2 times parsing of the json file.
But the better fix would be to actually use application/json and see/log what request.body() returns.
Postman simply doesn’t care about CORS headers. So CORS is just a browser concept and not a strong security mechanism. It allows you to restrict which other web apps may use your backend resources.
You have to just specifies CORS (Access-Control-Allow-Origin) headers.
I am trying to upload image to my api. Uploading with postman works fine. When I previously used expo go all worked fine as well. Once I switched to react native native code I get following error.
I am sure my endpoint is correct and it is HTTPS. Other post request that are not multipart/form data/PUT requests or Get requests work perfectly. Here is code:
const openImagePickerAsync = async () => {
let permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (permissionResult.granted === false) {
alert("Permission to access camera roll is required!");
return;
}
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 1,
});
console.log(result)
const { cancelled } = result as ImageInfo
if(cancelled == false)
{
const { uri } = result as ImageInfo
const bodyy = new FormData();
const bod = JSON.parse(JSON.stringify({ uri: uri, type: 'image/jpeg', name: 'profilePicture'+userId+".jpg" }));
bodyy.append('demo_image', bod);
try{
const log = await fetch("https://endpoint/api/upload/picture/" + userId, {
method:'POST',
body: bodyy,
headers: {
'Content-Type': 'multipart/form-data',
Accept: "application/json",
}
})
console.log(log)
Alert.alert("Profile picture uploaded!")
}
catch (error){
console.log(error)
}
}
else{
Alert.alert("You did not choose picture to upload.")
}
}
And here is response:
Edit1: I have also tried running my server on localhost with http and writing IPv4 address of my PC I am running server on (besides localhost:8000) I wrote ip:8000. Did not help..
your problem is about CORS (Cross-Origin Resource Sharing)
you must be add this command in your server
npm install express cors
const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors())
and cors() allowed the client to access this route
but if you didnt use express you can solve this problem with cors header request
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'multipart/form-data',
}```
This is my first time using react and node js. So for react, I have this form:
<form onSubmit={register}>
<label>First Name: </label>
<input required type="text" id="firstName" name="firstName" onChange={formValues}>
So here is what I wrote for the onChange function to update my hook
const formValues = (event) => {
setUserDetails({
...userDetails,
[event.target.name]: event.target.value
});
}
This is how I wrote my hook
const [userDetails, setUserDetails] = useState({
firstName: ''
});
For the onSubmit, this is how I wrote it
const register = async (event) => {
event.preventDefault();
const body = JSON.stringify({
firstName: userDetails.firstName
});
const response = await axios.post("/auth/register", body, {
header: {
'Content-Type': 'application/json'
}
});
}
Finally, when I try to console.log
console.log("the request body: ", req.body);
console.log("the first name", req.body.firstName);
Here are the results:
the request body: [Object: null prototype] {'{"firstName":"dawd"}': ''}
the first name undefined
This is confusing to me since I have the value for "req.body" but when I tried to get the value for the first name I get an undefined.
In my app.js which is my server, I have body-parser and it is still doesn't seem to fix the issue
const bodyParser = require('body-parser');
const express = require("express"); // Start server from nodejs
const app = express();
// Using body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
Any help would be greatly appreciated.
Unlike fetch, axios takes an object as body as pointed out by #Thanh. So when you convert your body into a string with JSON.stringify, axios converts it back into an object by taking the string and using it as the object's key, hence {'{"firstName":"dawd"}': ''}. You should pass an object directly into axios for it to be recived properly on the server side. So your register function should look like this:
const register = async (event) => {
event.preventDefault();
const body = {firstName: userDetails.firstName};
const response = await axios.post("/auth/register", body, {
header: {
'Content-Type': 'application/json'
}
});
}
While passing the data to axios you donot need to use JSON.stringify. You can directly pass the data as object.
const body = {
firstName: userDetails.firstName
};
axios post does not require stringify, so your body can be { firstName: userDetails.firstName }
I am trying to send a FormData containing a pdf file from a node.js script to a server. The request fail with a status code 400 in response. In the server side, it seems that he do not consider the data as a pdf file, or maybe that that the whole data is undefined.
This is what my node.js script does : (I've tried other ways to send the request but without success)
import axios from "axios"
import fs from "fs-extra"
import FormData from 'form-data'
const formData = new FormData()
const diplomaBuffer = await fs.readFile(`../data/fake/foo`+i+`.pdf`)
formData.append("diploma", diplomaBuffer)
formData.append("diplomaId", 1)
axios({
method: 'post',
processData: false,
contentType: 'multipart/form-data',
cache: false,
url: 'http://localhost:8080/certificates',
data: formData,
config: { headers: formData.getHeaders() }
})
// const response = await axios
// .post(`http://localhost:8080/certificates`, {
// body: formData
// })
// const response = await axios
// .post(`http://localhost:8080/certificates`
// , formData
// , {headers: formData.getHeaders()})
This is the function called in the server side :
app.post('/certificates', async (req, res) => {
const files = req.files
if(!files || !files.diploma || !files.diplomaId) {
res.status(400).send(new Error("No file provided"))
}
if(files.diploma.mimetype !== "application/pdf") {
res.status(400).send(new Error("diploma is not a pdf"))
}
// operations made with the received pdf (not relevant for the question)
const certificateId = uniqid()
const outputDiploma = `data/diplomas/${certificateId}.pdf`
await Promise.all([
fs.writeFile(outputDiploma, files.diploma.data),
])
await pdf.sign(outputDiploma, `${sha("sha256").update(certificateId + diplomaSecret).digest("hex")} : ${date()}`)
const diplomaBuffer = await fs.readFile(outputDiploma)
const certificate_hash = verify_pdf.hashBuffer(diplomaBuffer)
const certificateRegistry = await app.bizNetworkConnection.getAssetRegistry("consortium.supchain.assets.Certificate")
const certificate = app.factory.newResource("consortium.supchain.assets", "Certificate", certificateId)
certificate.diploma = app.factory.newRelationship("consortium.supchain.assets", "Diploma", req.body.diplomaId)
certificate.hashInfo = certificate_hash
await app.registry.certificate.add(certificate)
res.status(200).send("ok")
})