Cannot Post to backend Flask from Front End React Form - reactjs

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()

Related

AxiosError : Request failed with status code 400 (in React JS)

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));

Why when using React useState is the payload data clearing on a onsubmit event? This is a POST request to the server side

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

Display audio file in frontend through axios get request. Nodejs Reactjs Mongodb , multer , Gridfs

I used mongodb , gridfs ,multer to store the audio file.
And need to show the audio file in reactjs frontend through axios.
app.get('/songs/getsong/:filename', (req, res) => {
console.log(req.params.filename);
const file = gfs
.find({
filename: req.params.filename
})
.toArray((err, files) => {
if (!files || files.length === 0) {
return res.status(404).json({
err: "no files exist"
});
}
gfs.openDownloadStreamByName(req.params.filename).pipe(res);
console.log(files)
return res.status(200);
});
});
Display audio file in frontend
import React, { useEffect ,useState} from 'react'
import Header from '../header/Header';
import "./home.css"
import axios from 'axios';
export default function Home(){
const [audiofile , setAudioFile] = useState()
axios.get('http://localhost:8000/songs/getsong/379500b35576a4c49792718aebfc27ef.mp3')
.then((res) => {
console.log(res);
setAudioFile(res)
}).catch((error) => {
console.log(error)
});
return (
<div>
<Header />
<form action="http://localhost:8000/songs/upload" method="post" enctype="multipart/form-data">
<h1>File Upload</h1>
<input id="file" type="file" accept="audio/*" name='file' />
<input type="submit" />
</form>
<audio
controls
src={audiofile}>
Your browser does not support the
<code>audio</code> element.
</audio>
</div>
)
}
this is the file request
{
_id: 60774f8560de1713d4b06234,
length: 11756413,
chunkSize: 261120,
uploadDate: 2021-04-14T20:25:25.300Z,
filename: '379500b35576a4c49792718aebfc27ef.mp3',
md5: '36d8d712eb886859b07952e49d4de6a6',
contentType: 'audio/mpeg'
}
In postman it showed a audio file and it works fine. I need to show it
on frontend.
Check Postman response ss here
Thank you.
Locally, you can use the HTML5 Audio tag to play the audio, feed src with URL of the audio and , that's it.
its probably not playing because you are setting audio file with res instead of res.data, then confirm the format of the response. its usually in binary format so you need to convert before playing, you can blob() and URL.createObjectURL()

Trying to make website to update googlesheets

im trying to create a form to update googlesheets the project is in REACTJS, i followed this guide https://medium.com/#mheavers/storing-form-data-from-a-website-in-google-spreadsheets-using-javascript-react-a15aeb9143cb
now im facing a problem where i get the following error
error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
my code:
import React,{useState,useEffect} from 'react'
import {Form,Text} from 'informed'
import {gapi} from 'gapi-script'
const Forms=()=>{
const [SignInStatus, setSignInStatus] = useState("")
const SPREADSHEETID= ***my spreadsheet id**
const apiKey= ***my api key***
const clientID=***my oauth id***
const SCOPE=['https://www.googleapis.com/auth/spreadsheets']
const initClient=()=>{
console.log("got key")
gapi.client.init({
'apiKey': apiKey,
'clientId':clientID,
'scope': SCOPE,
'discoveryDocs': ['https://sheets.googleapis.com/$discovery/rest?version=v4'],
}).then(()=>{
gapi.auth2.getAuthInstance().isSignedIn.listen(SignInStatus)
setSignInStatus(gapi.auth2.getAuthInstance().isSignedIn.get())
})
}
const handleclientLoad=()=>{
gapi.load('client:auth2',initClient)
}
useEffect(() => {
handleclientLoad()
}, [])
const onFormSubmit=(submissionValues)=>{
console.log(submissionValues)
const params={
spreadsheetId:SPREADSHEETID,
clientId: clientID,
range:'Sheet1',
valueInputOption:'RAW',
insetDataOption:'INSERT_ROWS'
}
const valueRangeBody={
'majorDimension':'ROWS',
'VALUES':[submissionValues]
}
let request=gapi.client.sheets.spreadsheets.values.append(params,valueRangeBody)
request.then(function(response){
console.log(response.result)
},function (reason){
console.log(reason.result.error)
console.log("error: " + reason.result.error.message)
})
}
return(
<Form onSubmit={onFormSubmit}>
<label>
first name:
<Text field="name"/>
</label>
<button type="submit">Submit</button>
</Form>
)
}
export default Forms
This might not be what you want, but forms.google.com does exactly this already.

React redirect after login not working

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.

Resources