I am trying to post comments using axios. When I submit my datas entered in the form, I see this error in the console :
AxiosError {message: 'Request failed with status code 400', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
Here is my code :
import React, { useState } from 'react'
import TextField from '#material-ui/core/TextField';
import { Button } from '#material-ui/core'
import CommentsAPI from '../../Services/CommentsAPI'
export default function CommentForm() {
const [comment, setComment] = useState({})
const handleSubmit = async (event) => {
event.preventDefault();
try {
const data = CommentsAPI.create(JSON.stringify(comment))
console.log(data)
} catch (error) {
console.log(error)
}
}
const handleChange = (event) => {
const {name, value} = event.currentTarget
setComment({
...comment,
[name]: value
})
}
return (
<form onSubmit={handleSubmit}>
<div>
<TextField
id="pseudo"
label="Pseudo"
type="text"
onChange={handleChange}
name="pseudo"
/>
</div>
<div>
<TextField
id="outlined-multiline-static"
label="Comment"
multiline
minRows={2}
onChange={handleChange}
name="content"
/>
</div>
<div>
<Button variant="contained" color="primary" type="submit">
Send
</Button>
</div>
</form>
)
}
CommentsAPI.js file :
import { URL_COMMENTS } from '../config'
import axios from 'axios'
function create(comment) {
return axios.post(URL_COMMENTS, comment)
}
const CommentsAPI = {
create
}
export default CommentsAPI
I am trying to understand what is wrong. Thank you very much for your help !
Have a look on my server :
Collection type
Permission with POST api url
You're not sending anything to your API. CommentsAPI.create(YOUR COMMENT HERE)
const handleSubmit = async (event) => {
event.preventDefault();
try {
// const data = CommentsAPI.create() // WRONG !
// Create expects a comment, send something !
const data = CommentsAPI.create('This is a test');
// Or send the valu in your state
// const data = CommentsAPI.create(comment.content);
} catch (error) {
console.log(error)
}
}
Also, in your server you will need to return helpful error message. Like 'Hey, there is no message, please send a message in the payload'. That will help you understand better what's going on.
For anyone else, who is facing the same issue, try changing your get http request to post, if you are sending data from body that has a list.
Hope this helps.
If you receive a 400 (https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_client_errors), it means that the sever received your request but the content was not valid. Read the documentation of the API to be sure you send the correct payload.
By default, if Axios receives something other than a 2xx, it will throw an exception
And if you want your
console.log(data)
to work, do not forget to add await:
await console.log(data)
so that the code awaits the answer of the server before trying to console.log() it
Your problem is here...
JSON.stringify(comment)
This passes a string to Axios which it will interpret as text/plain and set the request content-type header to the same.
It's highly likely your API expects an application/json or application/x-www-form-urlencoded request body and rejects a plain text one.
To send the former, simply omit JSON.stringify() and let Axios deal with serialisation and content-type detection
// don't forget to `await`
const { data } = await CommentsAPI.create(comment);
The latter can be achieved using URLSearchParams
const { data } = await CommentsAPI.create(new URLSearchParams(comment));
Related
Background: I am trying to get my frontend code to connect with my backend code.
Steps Taken:
--set a breakpoint for on click event for the submit button: this returns with the event being fired however no Promise function ran and no response in the console.
--tested sending data on Postman to backend and get status 200 and works (backend is set correctly and is hosted on heroku)
--cleaned up POST request and set it as a promise and stored in Const Variable
Steps the code is taking:
First taking in string in each input field (name, email, question).
Using react usestate hook to use the current state and set the event.target.value as those values.
Once that is stored, then it gets stored in a variable called custData
One submit button is clicked it calls on the Variable that stores the Promise function for POST request using Axios
(This is where I believe something else is occurring)
*the POST is set to the correct backend URL with the values of custData which should be a string. Then it should return with response.data in the console and does not.
Below is my frontend react code for this component:
import React from 'react';
import {useState} from 'react';
import Axios from 'axios'
//import { response } from 'express';
const QuoteForm = () => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [question, setQuestion] = useState("");
//This is the promise version
const custData =
{
"name" :name ,
"email" :email ,
"question":question
} ;
const submitPromise= () => {
console.log(custData);
Axios.post('https://hookahsite-backend.herokuapp.com || https://localhost:8000 ' , custData)
.then( (axiosResponse)=> {
// here you can use the data
console.log(custData);
const submitQuestions = axiosResponse.data;
console.log(submitQuestions);
})
.catch((e)=> {console.log(e)})
}
//this uses try catch however the backend is not getting hit with any data
//tested this same request in Postman and it works
/*
function submitQuestion() {
try {
Axios.post('https://hookahsite-backend.herokuapp.com ' ,
{
name:name ,
email:email ,
question:question
},
)
}
catch (err) {console.error(err);}
}
*/
return(
<React.Fragment>
<form id="quoteForm"
>
<h1 id="quoteTitle">Quote Help Form</h1>
<p id="quotePar">Please provide your Name, Contact Email, and what products you would like more information about in this form :</p>
<label id="formName" className="Form">
Name:
<input type="text" name="name"
onChange={(event) => { setName(event.target.value);}}
/>
</label>
<label id="formEmail" className="Form">
Email:
<input type="text" name="email"
onChange={(event) => { setEmail(event.target.value);
}}/>
</label>
<br/>
<label id="formQuestion" className="Form" >
What products would you like to know more about:
<input type="text" name="help"
onChange={(event) => { setQuestion(event.target.value);
}}/>
</label>
<br/>
<br/>
<button id="quoteSubmit" type="submit"
onClick =
{
submitPromise
}
/*
old way
{()=>
submitQuestion()
}
*/
>Submit </button>
</form>
›
</React.Fragment>
)
};
export default QuoteForm;
(When I set the breakpoints this is a screen shot showing the promise is there and there seems to be an issue with data being sent as possible text and not json format)
**Any Further Help on this topic would greatly be appreciated. **
The problem I believe lies in your Axios post call.
Axios is used like this:
Axios.post('ENDPOINT_URL', BODY)
Here ENDPOINT_URL is the URL of your api endpoint that you want to send a post request to, however you are saying to axios that the endpoint url is:
'https://hookahsite-backend.herokuapp.com || https://localhost:8000'
As far as I am concerned there is no logic in axios so it is trying to hit that string literally with a post request.
You should move your OR logic to other place of the application.
For example using env variables to know that you are running locally or on heroku you could do something like:
let url;
if (process.env.SERVER_LOCATION === "LOCAL") {
url = "https://localhost:8000";
} else{
url = "https://hookahsite-backend.herokuapp.com";
}
axios.post(url, custData).then( // etc
Problem: I am trying to send data from React frontend to the backend server which is running flask. I am attempting to send data in which a user enters in information within a form, which is in react frontend.
My Attempt:
I know the backend and frontend are able to communicate because I have tested with a GET function as such:
#app.route('/get',methods=['GET'])
def testGet():
return {
'name': 'Hello World'
}
I tested this by using a hook within App.js, this is the following code:
useEffect(() => {
fetch('http://localhost:5000/get').then(response => {
if(response.ok){
return response.json()
}
}).then(data => console.log(data))
},[])
This useEffect correctly displays the message from the backend to the console. I am having a real hard time getting a POST to work. I get the following errors when attempting to POST. This error is on the backend:
The method is not allowed for the requested URL.
This is the error on the frontend:
Cannot POST /
The following code is what I am using to get input from the user to POST to the backend:
<form method="post">
<label>Input</label>
<input type="variable" name="variable" />
<input type="submit" value="Submit" />
</form>
This is the python backend function for the post:
#app.route('/post',methods=['POST'])
def testPost():
variable = request.form.get("variable","")
return jsonify(variable=variable)
I have researched a little and am following this post, but nothing seems to work.
How to send data from React to Flask then back to react and show output on DOM
Am I missing something really simple here?
The Form does the POST request on the root path '/', not on '/post'.
You have to specify the path to the form by setting the action attribute for example:
<form method="post" action='/post'>
<label>Input</label>
<input type="variable" name="variable" />
<input type="submit" value="Submit" />
</form>
Here is a post example react component:
import { useState } from "react"
function POST(path, data) {
return fetch(`http://localhost:5000${path}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}
)
}
function MyForm(props) {
const [text, setText] = useState('Input your name here');
const [name, setName] = useState('');
const onChange = e => {
setText(e.target.value)
}
const onClick = e => {
e.preventDefault();
POST('/post', {name: text}).then(
async (resp) => {
const json= await resp.json()
console.log(json.name)
setName(json.name)
}
)
}
return (
<div>
<form>
<label>Input</label>
<input value={text} onChange={onChange} />
<input type="submit" value="Submit" onClick={onClick} />
</form>
<p>Your name is: <b>{name}</b></p>
</div>
)
}
export default MyForm;
And here its corresponding backend with Flask 2.0:
from flask import Flask, request, jsonify, current_app
from flask_cors import CORS
app = Flask(__name__)
#app.post('/post')
def testPost():
name = request.json.get('name')
current_app.logger.debug(name)
return jsonify(name=name)
# because backend and frontend use different ports, we have to enable cross-origin requests
cors = CORS(app, resources={'/*':{'origins': 'http://localhost:3000'}})
if __name__ == "__main__":
app.run()
Background
When running my app over localhost, I can choose my PDF file and submit it. I'm able to get the path of the IPFS file and display the path in the console.
Problem
When adding this line to display my file, it doesn't work and shows "No PDF file specified" instead.
<Document src={https://ipfs.infura.io/ipfs/${this.state.ipfshash}} />
<Document file={https://ipfs.infura.io/ipfs/${this.state.docupayHash}} />
What I've Tried
I've gone to the link in Google Chrome (ipfs.infura.io/ipfs/"QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s") and I can see the file there, so I know the link is correct.
Code
App.js
import React, { Component } from "react";
import { Document, Page } from 'react-pdf';
import web3 from "./web3";
import ipfs from "./ipfs";
import storehash from "./storehash";
import "./styles/App.css";
class App extends Component {
state = {
contractHash: null,
buffer: "",
ethAddress: "",
blockNumber: "",
transactionHash: ""
};
captureFile = (event) => {
event.stopPropagation()
event.preventDefault();
const file = event.target.files[0];
let reader = new window.FileReader();
reader.readAsArrayBuffer(file);
reader.onloadend = () => this.convertToBuffer(reader);
};
convertToBuffer = async (reader) => {
// Convert file to buffer so that it can be uploaded to IPFS
const buffer = await Buffer.from(reader.result);
this.setState({buffer});
};
onClick = async () => {
try {
await web3.eth.getTransactionReceipt(this.state.transactionHash, (err, txReceipt) => {
console.log(err, txReceipt);
this.setState({txReceipt});
});
} catch (error) {
console.log(error);
}
}
onSubmit = async (event) => {
event.preventDefault();
// Take the user's MetaMask address
const accounts = await web3.eth.getAccounts();
console.log("Sending from Metamask account: " + accounts[0]);
// Retrieve the contract address from storehash.js
const ethAddress= await storehash.options.address;
this.setState({ethAddress});
// Save document to IPFS, return its hash, and set it to state
await ipfs.add(this.state.buffer, (err, contractHash) => {
console.log(err, contractHash);
this.setState({ contractHash: contractHash[0].hash });
storehash.methods.setHash(this.state.contractHash).send({ from: accounts[0] }, (error, transactionHash) => {
console.log(transactionHash);
this.setState({transactionHash});
});
})
};
render() {
return (
<div className="app">
<h3> Choose file to send to IPFS </h3>
<form onSubmit={this.onSubmit}>
<input type="file" onChange={this.captureFile} />
<button type="submit">Submit</button>
</form>
<Document file={`https://ipfs.infura.io/ipfs/${this.state.contractHash}`} />
<a href={`https://ipfs.infura.io/ipfs/${this.state.contractHash}`}>Click to download the file</a>
<button onClick = {this.onClick}>Get Transaction Receipt</button>
<p>IPFS Hash: {this.state.contractHash}</p>
<p>Contract Address: {this.state.ethAddress}</p>
<p>Tx Hash: {this.state.transactionHash}</p>
</div>
);
}
}
export default App;
MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16 <0.7.0;
contract MyContract {
string contractHash;
function setHash(string memory ipfsHash) public {
contractHash = ipfsHash;
}
function getHash() public view returns (string memory ipfsHash) {
return contractHash;
}
}
I've looked at other solutions on SO but none that I found were particularly related to my question. Thank you for your help and time!
Two things to try:
Add ?filename= parameter as a hint for both gateway and react-pdf:
<Document src={`https://ipfs.infura.io/ipfs/${this.state.ipfshash}?filename=test.pdf`} />
This will make content-type returned by the gateways more reliable and eliminate false-negatives in react-pdf.
Run your own gateway, or contact Infura and discuss raising request limits for your app.
FYI I've run below test multiple times:
$ curl -Ls 'https://dweb.link/ipfs/QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s?filename=test.pdf' > output && file output
output: PDF document, version 1.5
After a few times they stop returning PDF, instead they return HTML page with 429 Too Many Requests error:
output: HTML document, ASCII text, with CRLF line terminators
$ cat output
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>openresty</center>
</body>
</html>
It is very likely that react-pdf is unable to render your PDF because it gets 429 Too Many Requests error response instead of the PDF payload.
I want to show a notification with the upload status. I took over a project in React & ASP.NET and I am relatively new to this. The question is quite simple, yet I am struggling to solve it: How do I display a popup notification showing which files have been successfully been uploaded and which not?
import * as React from "react";
import { connect } from "react-redux";
import { Form, Select, Button, Upload, message, notification} from 'antd';
import * as Actions from "../actions";
const FormItem = Form.Item;
class UploadFileForm extends React.Component<any, any> {
constructor(props: any) {
super(props);
}
handleSubmit = (e) => {
message.config({ top: 0 });
message.loading('Importing in progress...', 3);
e.preventDefault();
this.props.uploadFile(this.props.form.getFieldsValue());
notification["info"]({
message: 'Files successfully uploaded',
description: '', // <-- this line has to be modified
duration: 10
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit}>
<FormItem label="File" >
{getFieldDecorator('upload', {
valuePropName: 'fileList',
getValueFromEvent: (e) => e.fileList.slice(-1)
})(
<Upload name="importFile" action={' '} multiple={false}>
<Button> Upload </Button>
</Upload>
)}
</FormItem>
<Button type="primary" htmlType="submit">Import</Button>
</Form>
);
}
}
export default Form.create()(UploadFileForm);
More specifically: How do I have to modify the line description: '', to show me a list of all uploaded files and their status as pure text, e.g. File(s) '1.txt', '2.txt', and '3.txt' have been successfully uploaded. File(s) '4.txt' failed.?
The project documentation says that we are using Redux-Saga, but I am not so maybe that makes the story easier.
I guess your this.props.uploadFile method is a promise so considering that you should show notification once that promise is resolved
this.props.uploadFile(this.props.form.getFieldsValue()).then(result => {
// since your client doesn't know which ones are success/failed, server should return
// this information when request is finished
const { successUploads, failUploads } = result;
notification["info"]({
message: 'Files successfully uploaded',
description: `File(s) ${successUploads.join(', ')} have been successfully uploaded. File(s) ${failUploads.join(', ')} failed.`
duration: 10
});
});
If you can't control whats returned from the server then you'd need to track uploads on client side, but that would mean having multiple uploads (requests) to the server and your upload method would look something like this:
async function uploadFiles(files) {
// I've called your server upload uploadService.send(), but replace this with your method
const results = await Promise.all(
files.map(file => uploadService.send(file))
.map(p => p.catch(e => e)
);
let successUploads = [];
let failUploads = [];
results.forEach((result, idx) => {
const file = files[idx];
if (result instanceof Error) {
failUploads.push(file);
} else {
successUploads.push(file);
}
});
return {
successUploads,
failUploads
}
}
Then you could call uploadFiles same way as shown in first snippet.
I am trying using react-router and redux to build my react app. However, I am unable to get the url to route back to the dashboard post login. Can somebody please point out my mistakes?
const form = reduxForm({
form: 'login'
});
class Login extends Component {
handleFormSubmit(formProps) {
this.props.loginUser(formProps);
var token = cookie.load('token');
if(token !== undefined){
const location = this.props.location;
if (location.state && location.state.nextPathname) {
browserHistory.push(location.state.nextPathname)
} else {
browserHistory.push('/')
}
}
}
renderAlert() {
if(this.props.errorMessage) {
return (
<div>
<span><strong>Error!</strong> Authentication error</span>
</div>
);
}
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
{this.renderAlert()}
<div>
<label>Username</label>
<Field name="username" className="form-control" component="input" type="text" />
</div>
<div>
<label>Password</label>
<Field name="password" className="form-control" component="input" type="password" />
</div>
<button type="submit" className="btn btn-primary submitButton">Login</button>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
message: state.auth.message
};
}
const mapDispatchToProps = dispatch => ({
loginUser: () =>
dispatch(loginUser);
});
export default connect(mapStateToProps, mapDispatchToProps)(form(Login));
My loginUser function is as below:
export function loginUser({ username, password }) {
return function(dispatch){
axios.post(`${AUTH_URL}/obtain-auth-token/`, { username, password}, {
headers: {
"X-CSRFToken": cookie.load('csrftoken')
}
})
.then(response => {
if('token' in response.data){
cookie.save('token', response.data.token, {path: '/'});
dispatch({type: AUTH_USER});
} else{
console.log("Error condiction: " + response);
errorHandler(dispatch, error.response, AUTH_ERROR);
}
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR);
});
}
}
This is my first react-redux project so the mistake might be pretty elementary. Would really appreciate your help!
The root of your issue appears to be with handling your async call - in your handleFormSubmit(formProps) function you have the following two lines:
this.props.loginUser(formProps);
var token = cookie.load('token');
You are dispatching your action that will be running your async function (loginUser(formProps) does a post using axios), and then you immediately try to consume the results of this async function by loading the token that it should have stored in a cookie upon success. This does not work because immediately upon running an async function, JavaScript will not wait for the results but instead will return to your handleFormSubmit function and run the rest of it through completion. I am sure that if you console.log'd your token it will be undefined (assuming there were no cookies before running the app) - the function continued without waiting for your async function.
So I know two good options that you can use to fix this:
Simple, standard solution: You run dispatch({type: AUTH_USER}); upon the success of your async post - have this action creator result in a change in your state held by redux (ex: loginFlag). Include loginFlag as a prop in your Login component (include it in your mapStateToProps function). Finally, include a componentWillReceiveProps(nextProps) lifecycle function to your Login component and have it handle the route change. Include something like:
componetWillReceiveProps(nextProps) {
if (nextProps.loginFlag) {
var token = cookie.load('token');
if(token !== undefined){
const location = this.props.location;
if (location.state && location.state.nextPathname) {
browserHistory.push(location.state.nextPathname)
} else {
browserHistory.push('/')
}
}
}
}
Or:
Heavier solution using another package: You can use the npm package react-router-redux (https://github.com/reactjs/react-router-redux), to do a push to browserHistory within your redux action at the end of your async call - doing so will require you to setup its middleware and hook it up to your redux store. More info: https://github.com/reactjs/react-router-redux#api
I would definitely recommend looking into react-router-redux. If you do not want the browserHistory functions available in your redux actions, then it is very simple to setup in your project (don't need to make changes to the store or add its middleware) and it allows you to track your url path within your redux state which can be very helpful.