Problem receiving Axios post data from reactjs app using Golang - reactjs

I have a locally hosted webpage using reactjs that sends an Axios post to port 9000. I have a Golang server listening to that port and it recieves the post. It then decodes the post but never gets any of its data. Below is the portion of code that sends the axios post in the reactjs app.
onSubmit = (event) => {
event.preventDefault();
let { task } = this.state;
console.log("printing new task title:", task);
if (task) {
axios
.post(
endpoint + "/api/task",
{task},
{headers: {"Content-Type": "application/x-www-form-urlencoded"}}
)
.then((res) => {
this.getTasks();
this.setState({task: ""});
console.log(res);
});
}
};
The below is the portion of the golang server that handles the post.
// CreateTask create task route
func CreateTask(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Context-Type", "application/x-www-form-urlencoded")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
var newTask listItem
_ = json.NewDecoder(r.Body).Decode(&newTask)
fmt.Println(newTask)
insertOneTask(newTask)
json.NewEncoder(w).Encode(newTask)
}
Below is the listItem struct
type listItem struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Title string `json:"title,omitempty"`
Completed bool `json:"completed,omitempty"`
}
I've tried renaming it to title instead of task, and just passing in a static variable but to no avail.
In the console it correctly prints the inputed text, but when the console outputs the axios response from the golang server, its response never includes the task name.
This is an example of what the data portion of the response from the golang server should look like: data: {_id: '000000000000000000000000', title:'Test'}.
It only ever outputs this data: {_id: '000000000000000000000000'}
The golang terminal output after the post is recieved is as follows:
{ObjectID("000000000000000000000000") false}
Inserted a Single Record ObjectID("63decde2a336b8e7cdc2b865")
It seems the task attribute is listed as '' in the new . My problem is the new task doesn't have the inputted text from the webpage. If you need more of the code it's below
Main Repository
ReactJS file
Main Go file

It's recommended to use DevTools to debug such kind of problem.
The screenshot shows that the payload is form-url-encoded. But the server tries to read it with a json decoder (_ = json.NewDecoder(r.Body).Decode(&newTask)). If you do not ignore the error from Decode, it should report that the content is not a valid json. To fix the issue, just remove {headers: {"Content-Type": "application/x-www-form-urlencoded"}} from client/src/To-Do-List.js.
After the change, the payload will be:
Other errors:
1. Context-Type header does not match the content in the response
The func CreateTask in go-server/main.go has another issue too. The response is encoded as json:
json.NewEncoder(w).Encode(newTask)
Which conflicts with:
w.Header().Set("Context-Type", "application/x-www-form-urlencoded")
The header should be replaced with:
w.Header().Set("Context-Type", "application/json")
2. The CORS is not set correctly
r.HandleFunc("/api/task", GetAllTasks).Methods("GET", "OPTIONS")
r.HandleFunc("/api/task", CreateTask).Methods("POST", "OPTIONS")
The OPTIONS requests will be handled by GetAllTasks. In order to allow the Content-Type header in the POST request, the following line should be added to GetAllTasks:
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
And since CreateTask does not handle the OPTIONS request, the following lines can be removed:
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

Related

How to use Axios.post() in a post request containing a json object request-body and a multipart form data (MP4)

Hi I Was wondering how I can send a a single axios post request containing a json object as the request body and also multipart form data (Mp4 file).
In my example I want to send 'details' and 'file'. I have tried sending details and file as 2nd and 3rd arguments to the axios.post() method but from what I can tell axios.post only accepts 2 args.
I have also tried appending the details and then the file, to the form data, but this does not work either.
If I split these into 2 seperate post calls, it works fine, but my application requires these to happen together.
I am getting the following error in my spring console:
[org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=----WebKitFormBoundaryqKyZ0R2SyFeDNCVp;charset=UTF-8' not supported]
Here is the error in my web dev tools console:
xhr.js:210 POST http://localhost:9191/api/123/file/upload 415
Id really appreciate any suggestions
const FileUpload = () => {
const [file, setFile]= useState(null)
const[details, setDetails] = useState({consent:false,
idConfirmed:false,
label:"",
roundId:""})
const changeHandler=(e)=>{
setFile(e.target.files[0]);
setDetails(prevDetails=>({
...prevDetails,
consent:true,
idConfirmed:true,
label:"test_Label"
}));
};
const handleSubmission=(e)=>{
e.preventDefault();
const data = new FormData();
data.append("file", file)
data.append("file", details)
console.log("Data: ", data)
axios.post(`${process.env.REACT_APP_URL_NEW_ROUND_VID}/123/file/upload`, data,
{
headers:{
"Content-Type":"multipart/form-data"
}
})
.then(res=>{
console.log("Data: ",res.data)
console.log("success")
})
.catch((e)=>{
console.log("Error", e)
})
//})
};
Here is my rest end point in Springboot:
#PostMapping(
path = "{patientId}/file/upload",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public void addWardRound(#PathVariable("patientId") String patientId,
#RequestParam("file") MultipartFile file,
#RequestBody WardRequest wardRequest){
WardRoundService.isFileEmpty(file);
WardRound round = service.saveRound(wardRequest);
String roundId = round.getRoundId();
service.uploadVid(patientId, roundId, file);
}
you can stringify and send the JSON data. but by using this method you need to parse it in the server!. it could be difficult
The best method is splitting it into 2 APIs
you can merge both API requests in UI by merging both APIs using promise.All
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
const promise1 = axios.get(URL1);
const promise2 = axios.post(URL2, data);
Promise.all([promise1, promise2]).then(function(values) {
console.log(values);
});
or else if you need to use the result of first API
then call the second API inside the response of first API itself

Blocked by CORS policy "...does not have HTTP ok status" (Amplify and ReactJS, AWS Gateway and Lambda)

I'm almost embarassed to be asking this question due to CORS support out there on SO but I can't get by:
Access to XMLHttpRequest at 'https://a93xxxxx.execute-api.eu-west-1.amazonaws.com/dev[object%20Object]' from origin 'https://www.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I've even published my React project with Amplify and attempted it from the real domain name to even eliminate anything to do with the development environment (Cloud 9 running npm version 6.14.8)
I've also made a test running Chrome with the --disable-web-security flag.
My Lambda function contains the following (out of the box stub)
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
// Uncomment below to enable CORS requests
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers" : "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
}
,
body: JSON.stringify("Hello from Lambda!")
};
return response;
};
Note that I've uncommented the CORS request part and the response statusCode is set to 200.
The code in my application that execute when a submission form is sent from the client:
uploadcontactusdata = async data => {
try {
console.log("Contact Us pressed")
const settings = {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
const fetchResponse = await API.post('econtactus', settings);
Notification({
title: 'Success',
message: 'Notification has been sent',
type: 'success'
});
}
catch (err) {
console.log("unable to send");
console.error(err)
}
}
I created the API Gateway + Lambda using Amplify (version 4.41.2). Not sure where else to look now. Any clues will be appreciated. Thanks
You can completely get past the need for api gateway by using appsync.
amplify add api
Choose graphql (I have not tried using rest but you shouldn't need it) choose the basic schema, edit it if you'd like, and publish. Once it's published you can create your own method. You can view this inside the AppSync UI under Schema.
type Mutation {
yourMethod(input: Input!): TableName <-- add your method to the list
}
Now inside Appsync choose Data Sources and add datasource. Give it a name, choose lambda as the type, then find your lambda in the list. Once it's added go back to your schema and find the method you created above. On the right side bar locate your method and click the attach link. Find the data source you just added. Fill out the region and lambda ARN. MAKE SURE you choose new role and not an existing one.
You might need to configure the request and response templates.
For request:
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson($context.args)
}
For response:
$util.toJson($context.result)
Now you can call your lambda directly from the UI and return your result without worrying about CORS or managing API Gateway.

Bad Request uploading file to hapi

I have followed many tutorials and done many combination but I still cannot get hapi to get the payload properly and I always get a 400 bad request reply.
This is the code in the frontend:
public uploadFile = (file: File) => {
console.log(file.name); /// <--- Correctly displays the name, so the files seems to be loaded correctly
const url = "/api/upload_resource";
const formData = new FormData();
formData.append('file', file);
formData.append('something', "else");
fetch(url, {
method: 'POST',
headers:{"Content-Type": "multipart/form-data"},
body: formData
})
.then((response: any) => { console.log(response);/* Done. Inform the user */ })
.catch((e) => { /* Error. Inform the user */ })
}
And this is the entry in server.route
{ path: "/api/upload_resource", method: "POST",
options:{handler: (e,h)=>{return h.response({}).code(200);},
payload:{ maxBytes: 30485760, parse: true, output:'file',
allow: ['multipart/form-data', "application/JSON"]
}
}
},
I'm using hapi 19.x
Solved,
TLDR:
remove the header in the fetch call and add another key multipart: {output: "file"} in options.payload in the server route item
Now this is how I figured out:
I managed to add a failAction method to the payload to be able to get a more verbose error
payload:{failAction: async (r:any, h:any, e:any) => {console.log(e.message);}, maxByt...
the failAction reported the following error
Invalid content-type header: multipart missing boundary
Ok, back to google to check what the heck a boundary is. After somehow managing to add a boundary, I got a new error message, clearly indicating progress
Unsupported Media Type
I was already familiar with that error message. And, according to many answers in other post, the solution seemed to be adding "Content-Type": "multipart/form-data" to the header. But that led to the "Bad Request Message" issue, and the posts referencing this error suggested to remove it.
So I knew the problem was the "Unsupported Media Type" error message and that the solution should be in the backend not the frontend. So I just searched "Unsupported Media Type hapi" and discovered the missing one payload option in the route.

Fetch status 200 but pending endllessly, except first call

I've been searching to solve this problem for a while but couldn't find a working solution.
I'm making a simple social network website and this API returns a article data such as text, image and video url, etc, all saved in server's local MySQL Database. My front-end is React and server is Nginx reverse proxy with Node.js using Express. When I load the page, I create 5 React components that each make fetch request for given article number.
The following code snippet is the fetch API that asks the server to fetch data from database:
//server-side script
app.get('/api/getArticle/:id', (req, res) => {
const con = mysql.createConnection({
host: 'myhost_name',
user: 'myUser',
password: 'myPassword',
database: 'myDB',
});
con.connect(function (err) {
if (err) {
throw err;
}
console.log("Connected!");
})
const idInterest = req.params.id.toString();
console.log(idInterest)
let sql = 'some_sql';
con.query(sql, function (err, result) {
if (err) {
res.status(500).send("Error while getting article data");
return;
}
else {
res.set('Connection', 'close')
res.status(200).send(result);
console.log("ended")
con.end();
return;
}
})
}
//React script
//index.js
fetch('http://mywebsite.com/api/getMaxArticleId/')//Retrieve top 5 article ID
.then((response) => {
for (let i = 0; i < data.length; i++) {
nodesList.push(<Container articleId={data[i]['id']}/>)
}
ReactDOM.render(<React.StrictMode><NavBar />{nodesList}<Writer writer="tempWriter" /></React.StrictMode>, document.getElementById('root'));
})
//Container.jsx; componentDidMount
const url = "http://mywebsite.com/api/getArticle/" + this.props.articleId.toString();
fetch(url, {
method: 'GET',
credentials: "include",
}).then((response) => {
response.json().then((json) => {
console.log(json);
//processing json data
This used to work very fine, but suddenly the getArticle/:id calls started to show 200 status but 'pending' in 'time' column in Chrome network tab, endlessly, all except the first*getArticle/:idcall. This prevents my subsequent .then() in each Container from being called and thus my entire tab is frozen.
Link to image of network tab
As you see from the image, all pending fetches are missing 'Content Download' and stuck in 'Waiting(TTFB)', except the first call, which was '39'
I checked the API is working fine, both on Postman and Chrome, the server sends result from DB query as expected, and first call's Json response is intact. I also see that console.log(response.json()) in React front-end shows Promise{<pending>} with *[[PromiseStatus]]: "Resolved"* and *[[PromiseValue]]* of Array(1) which has expected json data inside.
See Image
This became problematic after I added YouTube upload functionality with Google Cloud Platform API into my server-side script, so that looks little suspicious, but I have no certain clue. I'm also guessing maybe this could be problem of my React code, probably index.js, but I have no idea which specific part got me so wrong.
I've been working on this for a few days, and maybe I need common intelligence to solve this (or I made a silly mistake XD). So, any advices are welcomed :)

POST JSON with iron-ajax to SQL Server with Node

I am trying to return user data from a login with Polymer. I have it working with Postman, but am having trouble translating it into Polymer.
In Postman this returns a JSON object, but in Polymer it is returning undefined.
Polymer Client Code [Connecting to node.js server]
<iron-ajax id="ajaxUser"
url="http://localhost:8080/login"
method="post"
handle-as="json"
content-type="application/json"
headers='{"Access-Control-Allow-Origin": "*"}'
params="[[params]]"
on-response="saveUserCredentials"
last-response="{{user}}"></iron-ajax>
...
<paper-input id="username"></paper-input>
<paper-input id="password"></paper-input>
<paper-button on-tap="loginUser"></paper-button>
...
loginUser() {
this.params = {"username": this.$.username.value, "password": this.$.password.value};
console.log(this.params); // logs this.params as populated JSON
let request = this.$.ajaxUser.generateRequest();
request.completes.then(req => {
console.log(req); // logs <iron-request></iron-request>
console.log(this.user); // logs []
})
.catch(rejected => {
console.log(rejected.request); // not returned
console.log(rejected.error); // not returned
})
}
saveUserCredentials() {
console.log(this.user);
}
Node, Express, mssql Server Code [Connecting to SQL Server database]
app.post("/login", (req, res) => {
session.login(req, res)
})
...
exports.login = (req, res) => {
sql.connect(config.properties)
.then(pool => {
pool.request()
.input('user', sql.VarChar(50), req.body.username)
.input('password', sql.VarChar(50), req.body.password)
.query("SELECT role FROM Login WHERE username = #user AND password = #password")
.then(response => res.send(response))
.catch(err => res.send(err))
})
}
Error
SyntaxError: Unexpected token # in JSON at position 0 .
at JSON.parse ()
at createStrictSyntaxError (C:\node_modules\body-parser\lib\types\json.js:157:10)
at parse (C:\node_modules\body-parser\lib\types\json.js:83:15)
at C:\node_modules\body-parser\lib\read.js:121:18 .
at invokeCallback (C:\node_modules\raw-body\index.js:224:16)
at done (C:\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (C:\node_modules\raw-body\index.js:273:7)
at IncomingMessage.emit (events.js:159:13)
at endReadableNT (_stream_readable.js:1062:12)
at process._tickCallback (internal/process/next_tick.js:152:19)
The first issue appears to be that your server is expecting a JSON object in the request, but the server sees the request as a string due to a missing Content-Type header on your request. To set the Content-Type for JSON requests, set <iron-ajax>.contentType to application/json:
<iron-ajax content-type="application/json" ...>
OK, I was able to get a server response by setting content-type=application/json as an iron-ajax property. Am now getting Unexpected token # in JSON at position 0 as a server side error...
That sounds like the request is not actually valid JSON (since it contains a # as the first character). Use the Chrome DevTools Network Panel to inspect the actual contents of the payload. I wouldn't rely solely on console.log in your code.
Also, it is now making two requests when I submit. One with the content-type set, which resolves to 200, and one with that resolves to 400 with parsing error.
The first message is likely the preflight request, which is sent (as part of CORS) to the server to check whether content-type="application/json" is an allowed header. The second message is the intended data request, but that fails with the following error.
And a client side error of No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4001' is therefore not allowed access. The response had HTTP status code 400.
Your server needs to enable CORS requests. There are various ways to accomplish this, but the simplest Node solution might be to use the cors package:
const cors = require('cors');
const app = express();
app.use(cors());

Resources