I'm working with a reactjs frontend and springboot backend and I'm wondering how I can format/change form input data before I make a POST request to my backend. I'd like to maintain the dateFormat when displayed, but I'd like to change the value of the date to an ISO string before I make the POST request. New to react, so any discussion / help is appreciated.
import "./styles.css";
import DatePicker from 'react-datepicker';
import {useForm, Controller} from "react-hook-form";
import "react-datepicker/dist/react-datepicker.css";
export default function App() {
const {register, formState: { errors }, handleSubmit, control} = useForm();
const onSubmit = (data) => {
console.log(data.time.toISOString());
fetch('/test',
{ method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
}
);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="time"
control={control}
render={({ field: { onChange, value} }) => (
<DatePicker
onChange = {onChange}
selected= {value}
showTimeSelect
dateFormat="MMM d, yyyy h:mm aa"
/>
)}
rules={{ required:true}}
/>
<input type="submit"/>
</form>
);
}
You're so close! Instead of just logging the transformed data, submit it as part of your post request:
const onSubmit = (data) => {
const transformedTime = data.time.toISOString();
// make a copy of data, but overwrite the time property with our transformed time
const dataToSubmit = { ...data, time: transformedTime };
// submit the new dataToSubmit object as a part of the request
fetch('/test',
{ method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(dataToSubmit),
}
);
};
Related
I would like to add data to my database through the API url, with a form. But it's not really working. I'm kind of struggling to make it work. When I click on 'add' I check the array but nothing new is being added. I think i'm doing something wrong in the body variable. But not sure. How can i get the values from the form into the url API?
Here is my code:
export function AlbumForm(props) {
const [PostId, setPostId] = useState(0);
const {register, handleSubmit, errors, control} = useForm({
defaultValues: props.album
? props.album
: {
name: "",
artist: "",
imageUrl: "",
},
});
const useCreateAlbum =((data) => {
console.log(data);
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: data,
mode: 'cors'
};
fetch(process.env.REACT_APP_API_URL, requestOptions)
.then(response => response.json())
.then(data => setPostId(data.id));
});
return (
<div className="AlbumForm">
<h2>Album Form</h2>
<form onSubmit={handleSubmit(useCreateAlbum)}>
<input type="text" {...register("name")} placeholder="Name" name="name"/>
<input type="text" {...register("artist")} placeholder="Artist" name="artist" />
<input type="text" {...register("imageUrl")} placeholder="ImageUrl" name="imageUrl"/>
<button type="submit">{props.submitbutton}</button>
</form>
</div>
);
}
never mind I solved it. I just had to do this:
const useCreateAlbum =((data) => {
console.log(data);
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),/////here is the change
mode: 'cors'
};
fetch(process.env.REACT_APP_API_URL, requestOptions)
.then(response => response.json())
.then(data => setPostId(data.id));
});
I am posting to my API and where it should return a response nothing is logged to the console.
I have the following code:
import React, {useState} from "react"
import axios from 'axios';
const conn = axios.create({
baseURL: 'https://myurl.com',
headers: {
'Content-Type': 'application/json'
}
});
const Login = () => {
const [un,setUn] = useState('');
const [pw,setPw] = useState('');
const pleaseLogin = (un, pw) => {
//Hard coding the values for now
const email = "me#mydomain.org";
const pass = "weakpassword";
conn.post('/apilogin/authenticate/',
{
email: email, password: pass
})
.then(res => {
console.log("This is never reached");
console.log(res);
console.log(res.data);
}, (error) => {
console.log("This is reached if I alter the URL");
console.log(error);
});
}
return (
<form onSubmit={() => pleaseLogin(un,pw)}>
<label>
User Name:
<input type="text" value={un} onChange={setUn} />
</label>
<label>
Password:
<input type="text" value={pw} onChange={setPw} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
export default Login
If I change the URL then I get a 404 error so the error handling is working, but no response is logged at all if the URL is correct. If I run this call to the API through postman, then it works fine and I get a JSON response like below:
{
"refresh": "tokendata",
"access": "tokendata"
}
When trying using fetch, I was seeing CORS errors when logging the response, but since adding the cors setting it stopped, but I am still not seeing a response. There is no error on the server I can see, I just see 200OK when the request is processed.
This is using the Fetch method:
const pleaseLogin = async (un, pw) => {
const data = { email: un, password: pw };
fetch(`${baseUrl}/apilogin/authenticate/`, {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
mode: 'cors',
body: JSON.stringify(data),
})
.then(response => {console.log(response)});
}
I have tried running react from http://localhost:3000 and from http://myipaddress:3000 as well and that hasn't made a difference.
Server logs:
2020-12-03T10:04:37.365435+00:00 heroku[router]: at=info method=POST path="/apilogin/authenticate/" host=OMITTED fwd="myexternalipaddress" dyno=web.1 connect=0ms service=200ms status=200 bytes=761 protocol=https
2020-12-03T10:04:37.363496+00:00 app[web.1]: omitted - - [03/Dec/2020:10:04:37 +0000] "POST /apilogin/authenticate/ HTTP/1.1" 200 438 "http://localhost:3000/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
UPDATE: modified headers below.
headers: {
'Content-Type': 'application/json',
'Sec-Fetch-Site': 'cross-site',
},
UPDATE: I have tried running the react app on a development server where the API backend runs. The django application is set up to handle CORS. I have tried with Firefox and the server receives a request like below:
OPTIONS /api/authenticate/ HTTP/1.1" 200 0
If I run the app in Chrome then it gets nothing at all. I understand that this is a pre-flight request. As far as I can tell, the server is set up to handle this. I'm not sure where to go from here. I know that other frameworks work with the same request. I have tested with static HTML as below and this works.
<script>
const login = async () => {
const email = "emailaddress";
const pass = "password";
try {
const response = await axios({
'Content-Type' : 'application/json',
url: 'http://127.0.0.1:8000/api/authenticate/',
method: 'post',
data: { email: email, password: pass },
}).then( response => {
console.log(response);
});
} catch (e) {
console.log(e);
}
}
login();
</script>
try add "proxy": "http://localhost:3000" to your package.json inside your client folder (to overcome CORS issues).:-
/client/package.json
{
"name": "proj_name",
"version": "0.1.0",
"proxy": "http://localhost:3000"
}
Then, add async await to your res (a lot of times I do stumble upon this issue & this helps me):-
Login.js:-
import React, {useState} from "react"
import axios from 'axios';
const conn = axios.create({
baseURL: 'https://myurl.com',
headers: {
'Content-Type': 'application/json'
}
});
const Login = () => {
const [un,setUn] = useState('');
const [pw,setPw] = useState('');
const sayHello = () => {
console.log("hello");
}
const pleaseLogin = (un, pw) => {
//Hard coding the values for now
const email = "me#mydomain.org";
const pass = "weakpassword";
conn.post('/apilogin/authenticate/',
{
email: email, password: pass
})
.then(async res => {
const result = await res
console.log("This is never reached");
console.log(result);
console.log(result.data);
}, (error) => {
console.log("This is reached if I alter the URL");
console.log(error);
});
}
return (
<form onSubmit={() => pleaseLogin(un,pw)}>
<label>
User Name:
<input type="text" value={un} onChange={setUn} />
</label>
<label>
Password:
<input type="text" value={pw} onChange={setPw} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
export default Login
I don't quite understand when you stated it works with static HTML in your updates above.
I seems like these part of code (show below) might have contribute to your issue:-
// this part
const conn = axios.create({
baseURL: 'https://myurl.com',
headers: {
'Content-Type': 'application/json'
}
});
// and this part
conn.post('/apilogin/authenticate/',
{
email: email, password: pass
})
.then(res => {
console.log("This is never reached");
console.log(res);
console.log(res.data);
}, (error) => {
console.log("This is reached if I alter the URL");
console.log(error);
});
If you build your axios around that cause of wanting it to be dynamic. Then you could change it to your own static HTML updates (which I think can also can be dynamic with some changes):-
Login.js:-
import React, {useState} from "react"
import axios from 'axios';
const baseUrl = 'https://myurl.com'
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const Login = () => {
const [un,setUn] = useState('');
const [pw,setPw] = useState('');
const sayHello = () => {
console.log("hello");
}
const pleaseLogin = async(un, pw) => {
//Hard coding the values for now
const email = "me#mydomain.org";
const pass = "weakpassword";
// the BASEURL & CONFIG can still be DYNAMIC
await axios.post(baseUrl + '/apilogin/authenticate/',
{
email: email, password: pass
}, config)
.then(async res => {
const result = await res
console.log("This is never reached");
console.log(result);
console.log(result.data);
})
.catch(error => {
console.log("This is reached if I alter the URL");
console.log(error);
});
}
return (
<form onSubmit={() => pleaseLogin(un,pw)}>
<label>
User Name:
<input type="text" value={un} onChange={setUn} />
</label>
<label>
Password:
<input type="text" value={pw} onChange={setPw} />
</label>
<input type="submit" value="Submit" />
</form>
);
};
export default Login
I'm trying to submit some simple data to my server. Everything works fine - on my desktop. When i try on my mobile (same network) it does not work. The page just refreshes but the POST request fails (not sure if it fails because i can't open a console on my mobile). It seems like the page refreshes before it actually gets to the fetch command but i'm not sure and not sure how to troubleshoot because it is working fine on my desktop. Please help! Thanks :)
const onSubmit = (event) => {
const data = {
price: price,
gallons: gallons,
miles: miles,
notes: notes,
source: params.car_source,
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
};
fetch(`${process.env.REACT_APP_API_BASE_URL}/consumption/`, options);
};
This will work! Try this out, Just added event.preventDefault() in handleSubmit function.
I follow this link for Debugging in mobile.
import React, { useState } from 'react';
function NameForm() {
const [name, setName] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
const data = {
price,
gallons,
miles,
notes,
source: params.car_source,
};
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
};
fetch(`${process.env.REACT_APP_API_BASE_URL}/consumption/`, options);
};
const handleChange = (event) => {
setName(event.target.value);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
export default NameForm;
I want to get input value and put them in json
but I don't know how to get input value in this case
if I tried document.querySelector("todo-text").value
It turns out to error.
const NewTodo: React.FC<NewTodoProps> = (props) => {
/* const textInputRef = useRef<HTMLInputElement>(null); */
const formData = new FormData();
const photos = document.querySelector('input[type="text"][multiple]');
const data = {
word: document.querySelector<HTMLInputElement>("todo-text").value,
meaning: document.getElementById("todo-meaning"),
};
const handleSubmit = async () => {
const body = JSON.stringify(data);
debugger;
const response = await fetch("https://localhost:5001/api/vocas", {
method: "POST", // *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",
Accept: "*/*",
},
//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: body, // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります
});
debugger;
const result = await response.json();
console.log(result);
return;
};
return (
<form onSubmit={handleSubmit}>
<div className="form-control">
<label htmlFor="todo-text">単語:</label>
<input type="text" id="todo-text" />
<br />
<label htmlFor="todo-meaning">意味:</label>
<input type="text" id="todo-meaning" />
</div>
<button type="submit">追加</button>
</form>
);
};
in this case, how to get input value??
We need to use useState to get the capture the userinput
import {useState} from 'react';
const[toDoText,setToDoText] = useState("");
in input field add a onChange Event
const handleInput = (event : React.ChangeEvent<HTMLInputElement>) => {
let target = event.target;
setToDoText((currentState) => {
return target.value;
})
}
<input type="text" id="todo-text" onChange={handleInput} />
We want to send an image file as multipart/form to the backend, we try to use html form to get file and send the file as formData, here are the codes
export default class Task extends React.Component {
uploadAction() {
var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
headers: {
"Content-Type": "multipart/form-data"
"Accept": "application/json",
"type": "formData"
},
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
}
render() {
return (
<form encType="multipart/form-data" action="">
<input type="file" name="fileName" defaultValue="fileName"></input>
<input type="button" value="upload" onClick={this.uploadAction.bind(this)}></input>
</form>
)
}
}
The error in backend is "nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found".
After reading this, we tried to set boundary to headers in fetch:
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
headers: {
"Content-Type": "multipart/form-data; boundary=AaB03x" +
"--AaB03x" +
"Content-Disposition: file" +
"Content-Type: png" +
"Content-Transfer-Encoding: binary" +
"...data... " +
"--AaB03x--",
"Accept": "application/json",
"type": "formData"
},
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
}
This time, the error in backend is: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
Do we add the multipart boundary right? Where should it be?
Maybe we are wrong at first because we don't get the multipart/form-data. How can we get it correctly?
We just try to remove our headers and it works!
fetch("http://localhost:8910/taskCreationController/createStoryTask", {
mode: 'no-cors',
method: "POST",
body: data
}).then(function (res) {
if (res.ok) {
alert("Perfect! ");
} else if (res.status == 401) {
alert("Oops! ");
}
}, function (e) {
alert("Error submitting form!");
});
Here is my solution for image upload with preview through axios.
import React, { Component } from 'react';
import axios from "axios";
React Component Class:
class FileUpload extends Component {
// API Endpoints
custom_file_upload_url = `YOUR_API_ENDPOINT_SHOULD_GOES_HERE`;
constructor(props) {
super(props);
this.state = {
image_file: null,
image_preview: '',
}
}
// Image Preview Handler
handleImagePreview = (e) => {
let image_as_base64 = URL.createObjectURL(e.target.files[0])
let image_as_files = e.target.files[0];
this.setState({
image_preview: image_as_base64,
image_file: image_as_files,
})
}
// Image/File Submit Handler
handleSubmitFile = () => {
if (this.state.image_file !== null){
let formData = new FormData();
formData.append('customFile', this.state.image_file);
// the image field name should be similar to your api endpoint field name
// in my case here the field name is customFile
axios.post(
this.custom_file_upload_url,
formData,
{
headers: {
"Authorization": "YOUR_API_AUTHORIZATION_KEY_SHOULD_GOES_HERE_IF_HAVE",
"Content-type": "multipart/form-data",
},
}
)
.then(res => {
console.log(`Success` + res.data);
})
.catch(err => {
console.log(err);
})
}
}
// render from here
render() {
return (
<div>
{/* image preview */}
<img src={this.state.image_preview} alt="image preview"/>
{/* image input field */}
<input
type="file"
onChange={this.handleImagePreview}
/>
<label>Upload file</label>
<input type="submit" onClick={this.handleSubmitFile} value="Submit"/>
</div>
);
}
}
export default FileUpload;
The file is also available in the event:
e.target.files[0]
(eliminates the need for document.querySelector('input[type="file"]').files[0];)
uploadAction(e) {
const data = new FormData();
const imagedata = e.target.files[0];
data.append('inputname', imagedata);
...
Note:
Use console.log(data.get('inputname')) for debugging, console.log(data) will not display the appended data.
https://muffinman.io/uploading-files-using-fetch-multipart-form-data/ worked best for me. Its using formData.
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Button from "react-bootstrap/Button";
const ReactDOM = require("react-dom");
export default class App extends React.Component {
constructor(props) {
super(props);
this.test = this.test.bind(this);
this.state = {
fileUploadOngoing: false
};
}
test() {
console.log(
"Test this.state.fileUploadOngoing=" + this.state.fileUploadOngoing
);
this.setState({
fileUploadOngoing: true
});
const fileInput = document.querySelector("#fileInput");
const formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("test", "StringValueTest");
const options = {
method: "POST",
body: formData
// If you add this, upload won't work
// headers: {
// 'Content-Type': 'multipart/form-data',
// }
};
fetch("http://localhost:5000/ui/upload/file", options);
}
render() {
console.log("this.state.fileUploadOngoing=" + this.state.fileUploadOngoing);
return (
<div>
<input id="fileInput" type="file" name="file" />
<Button onClick={this.test} variant="primary">
Primary
</Button>
{this.state.fileUploadOngoing && (
<div>
<h1> File upload ongoing abc 123</h1>
{console.log(
"Why is it printing this.state.fileUploadOngoing=" +
this.state.fileUploadOngoing
)}
</div>
)}
</div>
);
}
}
React File Upload Component
import { Component } from 'react';
class Upload extends Component {
constructor() {
super();
this.state = {
image: '',
}
}
handleFileChange = e => {
this.setState({
[e.target.name]: e.target.files[0],
})
}
handleSubmit = async e => {
e.preventDefault();
const formData = new FormData();
for (let name in this.state) {
formData.append(name, this.state[name]);
}
await fetch('/api/upload', {
method: 'POST',
body: formData,
});
alert('done');
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="image"
type="file"
onChange={this.handleFileChange}>
</input>
<input type="submit"></input>
</form>
)
}
}
export default Upload;
the request was rejected because no multipart boundary was found".
When you send multipart/form-data, the boundary is automatically added to a content-type of a request header. you have to tell the server when the parameter ends with the boundary rule. You had to set the Content-type like this
"Content-Type": `multipart/form-data: boundary=add-random-characters`
This article with guide you: https://roytuts.com/boundary-in-multipart-form-data/
The boundary is included to separate name/value pair in the
multipart/form-data. The boundary parameter acts like a marker for
each pair of name and value in the multipart/form-data. The boundary
parameter is automatically added to the Content-Type in the http
(Hyper Text Transfer Protocol) request header.
For sending multipart/formdata, you need to avoid contentType, since the browser automatically assigns the boundary and Content-Type.
In your case by using fetch, even if you avoid Content-Type it sets to default text/plain. So try with jQuery ajax. which removes the contentType if we set it to false.
This is the working code
var data = new FormData();
var imagedata = document.querySelector('input[type="file"]').files[0];
data.append("data", imagedata);
$.ajax({
method: "POST",
url: fullUrl,
data: data,
dataType: 'json',
cache: false,
processData: false,
contentType: false
}).done((data) => {
//resolve(data);
}).fail((err) => {
//console.log("errorrr for file upload", err);
//reject(err);
});