How to Upload Images using React, fetch, and Django REST - javascript

I'm encountering a bit of a roadblock in my dev work. I'm trying to upload a photo that I'm sending using FormData in fetch. I'm guessing my problem is in my content header or my back-end handling. Eitherway, I can't seem to find a way around it. I hope you guys can help me
general.js - this is my handler for a request
export const postDataWithImage = (url, data) => {
return fetch(url, {
body: data, // must match 'Content-Type' header
credentials: 'same-origin', //pass cookies, for authentication
method: 'POST',
headers: {
'Accept': 'application/json, application/xml, text/plain, text/html, *.*',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
},
})
.then(response => response.json()); // parses response to JSON
};
user-creation.js - my actual usage of the function above (sending multiple data)
heres an image of the data I'm sending
![1] https://imgur.com/leBlC7L
const data = {...this.state, ...form};
const formData = new FormData();
Object.entries(data).forEach(([key, value]) => formData.append(key, value));
postDataWithImage('/users', data)
.then(data => {
if (data.error) {
console.log("theres an error");
this.setState({
error: data["error"]
});
console.log(this.state.error);
} else {
console.log(data["data"]);
}
})
.catch(error => message.warning(error.message));
views.py - my backend handler using Django REST not: this returns me an error either byte has no attribute 'get'... or an empty ModelDict for request.FILES
#staticmethod
def post(request):
print(request.body.get('image'))
print(request.FILES)
if "username" not in request.data or "password" not in request.data:
return Response(data={
"error": "Missing username or password"
}, status=400, content_type="application/json")
return Response(data=data, status=200, content_type="application/json")
Please help me I'm really stuck. Thank you!

I faced similar problem using Vue.js and Django.
Finally I noticed that the problem was that: boundary was not set to the header.
The solution is to remove headers from your request like this:
fetch(url, {
body: data, // assume this is some binary data
method: 'POST',
})
Then, your browser will automatically add proper headers for your request. And you will see boundary field which is added by your browser in your request headers.

Try to remove the "Content-Type" from the headers of fetch

Related

Not receiving data on server when doing an API POST call from the client

Already checked the endpoint with Insomnia and is working fine, but when trying to connect with the backend from the client there is some kind of problem. The connection between the client and the server is done this way:
const uri = `${basePath}/${baseVersion}/sign-up`;
const params = {
method: "POST",
body: JSON.stringify(data),
header: {
"Content-Type": "application/json"
}
};
And if I show in the console params object this is what is inside it:
enter image description here
Just to clarify, there isn't a CORS problem as I am using a Google Chrome extension for it.
This is the response of the fecth:
enter image description here
Is your problem not receiving a response from the server in the promise? If so, that is because there is no code in your snippet that actually returns the data. (Sorry if I misidentified the problem, I don't have the ability to comment)
const uri = `${basePath}/${baseVersion}/sign-up`;
async function fetchPost(data = {}) {
var response = await fetch(uri,
method: "POST",
mode: "cors",
header: {
"Content-Type": "application/json"
},
referrerPolicy: "strict-origin-when-cross-origin" //you can replace that with anything you want depending on the situation
body: JSON.stringify(data)
});
// if you're expecting the response to be json, use the below, but if you want it in text, then do response.text, etc.
return response.json();
}
fetchPost();

"No Access control header is present" - while calling AWS lambda function through API

I have created a Lambda function in python, inside this function I have put header with cross origin details. Here is the code of my Lambda function:
def lambda_handler(event, context):
data=''
s3_boto = boto3.client('s3')
s3 = boto3.resource('s3')
reference_elements = event['data1']
test_elements = event['data2']
try:
#access first event object
imagePath = []
data= compute_data(reference_elements, test_elements)
return response({'message': data}, 200)
except Exception as e:
return e
return response({'message': data}, 200)
def response(message, status_code):
return {
'statusCode': str(status_code),
'body': json.dumps(message),
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
}
Now I have created a AWS API Gateway with POST method and enable CORS. Then I deployed this API.
While I am running this API from Postman it is working fine.
But once I try to fetch this API from my React JS code it is throwing the following error:
enter image description here
Here is my React Js code in button click:
let postData = {"key1":"value1","key2":"value2","key3":"value3"}
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Headers": "*"
},
body: JSON.stringify(postData)
}
const url =
"https://apiId.execute-api.us-east-1.amazonaws.com/apiFunctionName"
fetch(url, requestOptions)
.then(response => console.log(response))
.then(data => console.log(data))
Please correct me where I am going wrong, I have invested lots of time for trial and error but no luck.
Browsers before making a call to a cross origin, makes a preflight request using the OPTIONS method. In your network tab, you should be seeing an OPTIONS call, which in your case is not allowed on ApiGateway. Similar to the POST method you have allowed on gateway, allow OPTIONS call. Return the OPTIONS call with a 200 and the CORS headers, based on which domain you want to allow.

Axios - DELETE Request With Request Body and Headers?

I'm using Axios while programming in ReactJS and I pretend to send a DELETE request to my server.
To do so I need the headers:
headers: {
'Authorization': ...
}
and the body is composed of
var payload = {
"username": ..
}
I've been searching in the inter webs and only found that the DELETE method requires a "param" and accepts no "data".
I've been trying to send it like so:
axios.delete(URL, payload, header);
or even
axios.delete(URL, {params: payload}, header);
But nothing seems to work...
Can someone tell me if it's possible (I presume it is) to send a DELETE request with both headers and body and how to do so?
So after a number of tries, I found it working.
Please follow the order sequence it's very important else it won't work
axios.delete(URL, {
headers: {
Authorization: authorizationToken
},
data: {
source: source
}
});
axios.delete does supports both request body and headers.
It accepts two parameters: url and optional config. You can use config.data to set the request body and headers as follows:
axios.delete(url, { data: { foo: "bar" }, headers: { "Authorization": "***" } });
See here - https://github.com/axios/axios/issues/897
Here is a brief summary of the formats required to send various http verbs with axios:
GET: Two ways
First method
axios.get('/user?ID=12345')
.then(function (response) {
// Do something
})
Second method
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
// Do something
})
The two above are equivalent. Observe the params keyword in the second method.
POST and PATCH
axios.post('any-url', payload).then(
// payload is the body of the request
// Do something
)
axios.patch('any-url', payload).then(
// payload is the body of the request
// Do something
)
DELETE
axios.delete('url', { data: payload }).then(
// Observe the data keyword this time. Very important
// payload is the request body
// Do something
)
Key take aways
get requests optionally need a params key to properly set query parameters
delete requests with a body need it to be set under a data key
axios.delete is passed a url and an optional configuration.
axios.delete(url[, config])
The fields available to the configuration can include the headers.
This makes it so that the API call can be written as:
const headers = {
'Authorization': 'Bearer paperboy'
}
const data = {
foo: 'bar'
}
axios.delete('https://foo.svc/resource', {headers, data})
For those who tried everything above and still don't see the payload with the request - make sure you have:
"axios": "^0.21.1" (not 0.20.0)
Then, the above solutions work
axios.delete("URL", {
headers: {
Authorization: `Bearer ${token}`,
},
data: {
var1: "var1",
var2: "var2"
},
})
You can access the payload with
req.body.var1, req.body.var2
Here's the issue:
https://github.com/axios/axios/issues/3335
For Delete, you will need to do as per the following
axios.delete("/<your endpoint>", { data:<"payload object">})
It worked for me.
I had the same issue I solved it like that:
axios.delete(url, {data:{username:"user", password:"pass"}, headers:{Authorization: "token"}})
Actually, axios.delete supports a request body.
It accepts two parameters: a URL and an optional config. That is...
axios.delete(url: string, config?: AxiosRequestConfig | undefined)
You can do the following to set the response body for the delete request:
let config = {
headers: {
Authorization: authToken
},
data: { //! Take note of the `data` keyword. This is the request body.
key: value,
... //! more `key: value` pairs as desired.
}
}
axios.delete(url, config)
I hope this helps someone!
If we have:
myData = { field1: val1, field2: val2 }
We could transform the data (JSON) into a string then send it, as a parameter, toward the backend:
axios.delete("http://localhost:[YOUR PORT]/api/delete/" + JSON.stringify(myData),
{ headers: { 'authorization': localStorage.getItem('token') } }
)
In the server side, we get our object back:
app.delete("/api/delete/:dataFromFrontEnd", requireAuth, (req, res) => {
// we could get our object back:
const myData = JSON.parse(req.params.dataFromFrontEnd)
})
Note: the answer from "x4wiz" on Feb 14 at 15:49 is more accurate to the question than mine! My solution is without the "body" (it could be helpful in some situation...)
Update: my solution is NOT working when the object has the weight of 540 Bytes (15*UUIDv4) and more (please, check the documentation for the exact value). The solution of "x4wiz" (and many others above) is way better. So, why not delete my answer? Because, it works, but mostly, it brings me most of my Stackoverflow's reputation ;-)
i found a way that's works:
axios
.delete(URL, {
params: { id: 'IDDataBase'},
headers: {
token: 'TOKEN',
},
})
.then(function (response) {
})
.catch(function (error) {
console.log(error);
});
I hope this work for you too.
To send an HTTP DELETE with some headers via axios I've done this:
const deleteUrl = "http//foo.bar.baz";
const httpReqHeaders = {
'Authorization': token,
'Content-Type': 'application/json'
};
// check the structure here: https://github.com/axios/axios#request-config
const axiosConfigObject = {headers: httpReqHeaders};
axios.delete(deleteUrl, axiosConfigObject);
The axios syntax for different HTTP verbs (GET, POST, PUT, DELETE) is tricky because sometimes the 2nd parameter is supposed to be the HTTP body, some other times (when it might not be needed) you just pass the headers as the 2nd parameter.
However let's say you need to send an HTTP POST request without an HTTP body, then you need to pass undefined as the 2nd parameter.
Bare in mind that according to the definition of the configuration object (https://github.com/axios/axios#request-config) you can still pass an HTTP body in the HTTP call via the data field when calling axios.delete, however for the HTTP DELETE verb it will be ignored.
This confusion between the 2nd parameter being sometimes the HTTP body and some other time the whole config object for axios is due to how the HTTP rules have been implemented. Sometimes an HTTP body is not needed for an HTTP call to be considered valid.
For Axios DELETE Request, you need to include request payload and headers like this under one JSON object:
axios.delete(URL, {
headers: {
'Authorization': ...
},
data: {
"username": ...
}
})
Why can't I do it easily as I do similar to POST requests?
Looking at the Axios documentation, we see that the methods for .get, .post... have a different signature:
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
Notice how only post, patch and put have the data parameter. This is because these methods are the ones that usually include a body.
Looking at RFC7231, we see that a DELETE request is not expected to have a body; if you include a body, what it will mean is not defined in the spec, and servers are not expected to understand it.
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.
(From the 5th paragraph here).
In this case, if you are also in control of the server, you could decide to accept this body in the request and give it whatever semantics you want. May be you are working with somebody else's server, and they expect this body.
Because DELETE requests with bodies are not defined in the specs, and because they're not common, Axios didn't include them in those method aliases. But, because they're possible, you can do it, just takes a bit more effort.
I'd argue that it would be more conventional to include the information on the url, so you'd do:
axios.delete(
`https://example.com/user/${encodeURIComponent(username}`,
{ headers: ... }
)
or, if you want to be able to delete the user using different criteria (sometimes by username, or by email, or by id...)
axios.delete(
`https://example.com/user?username=${encodeURIComponent(username)}`,
{ headers: ... }
)
Not realated to axios but might help people tackle the problem they are looking for. PHP doesn't parse post data when preforming a delete call. Axios delete can send body content with a request.
example:
//post example
let url = 'http://local.test/test/test.php';
let formData = new FormData();
formData.append('asdf', 'asdf');
formData.append('test', 'test');
axios({
url: url,
method: 'post',
data: formData,
}).then(function (response) {
console.log(response);
})
result: $_POST Array
(
[asdf] => asdf
[test] => test
)
// delete example
axios({
url: url,
method: 'delete',
data: formData,
}).then(function (response) {
console.log(response);
})
result: $_POST Array
(
)
to get post data on delete call in php use:
file_get_contents('php://input');
axios.post('/myentity/839', {
_method: 'DELETE'
})
.then( response => {
//handle success
})
.catch( error => {
//handle failure
});
Thanks to:
https://www.mikehealy.com.au/deleting-with-axios-and-laravel/
I encountered the same problem...
I solved it by creating a custom axios instance. and using that to make a authenticated delete request..
const token = localStorage.getItem('token');
const request = axios.create({
headers: {
Authorization: token
}
});
await request.delete('<your route>, { data: { <your data> }});
I tried all of the above which did not work for me. I ended up just going with PUT (inspiration found here) and just changed my server side logic to perform a delete on this url call. (django rest framework function override).
e.g.
.put(`http://127.0.0.1:8006/api/updatetoken/20`, bayst)
.then((response) => response.data)
.catch((error) => { throw error.response.data; });
Use {data: {key: value}} JSON object, the example code snippet is given below:
// Frontend Code
axios.delete(`URL`, {
data: {id: "abcd", info: "abcd"},
})
.then(res => {
console.log(res);
});
// Backend Code (express.js)
app.delete("URL", (req, res) => {
const id = req.body.id;
const info = req.body.info;
db.query("DELETE FROM abc_table WHERE id=? AND info=?;", [id, info],
(err, result) => {
if (err) console.log(err);
else res.send(result);
}
);
});
Axios DELETE request does supports similar what POST request does, but comes in different formats.
DELETE request payload sample code:
axios.delete(url, { data: { hello: "world" }, headers: { "Authorization": "Bearer_token_here" } });
POST request payload sample code:
axios.post(url, { hello: "world" }, { headers: { "Authorization": "Bearer_token_here" } });
Noticed that { hello: "world" } is configured in different ways, but both performs same functions.
this code is generated from post man and it's perfectly work for delete api request with body.
var data = JSON.stringify({"profile":"false","cover":"true"});
var config = {
method: 'delete',
url: 'https://api.fox.com/dev/user/image',
headers: {
'Authorization': 'Bearer token',
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});

posting form data with Axios to .NET API

I'm using React and Axios to post formData to an internal .NET API.
The API is expecting data like this:
[HttpPost("upload")]
public virtual async Task<IActionResult> PostMulti(Int64 parentId, ICollection<IFormFile> fileData)
{
foreach (var file in fileData) {
await SaveFile(file, parent);
}
return Created("", Map(Repository.Get(parentId)));
}
When I step through the debugger, the count for "fileData" is always 0.
Here is how I'm sending it using Axios:
const formData = new FormData();
formData.append('image', this.state.file);
console.log("this.state.file = ", this.state.file);
console.log("formData = ", formData);
axios({
url: `/api/gameMethods/playerStates/${this.props.playerId}/files/upload`,
method: 'POST',
data: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data'
}
})
.then((response) => {
//handle success
console.log('response -- then: ', response);
this.setState({
file: this.state.file
});
})
.catch((response) => {
//handle error
console.log('response -- catch: ', response);
});
I use console.log for debugging. It shows me the file object when I write it out(name, size, etc).
It also fires in the ".then" handler of the method and shows this:
"response -- then: data: Array(0), status: 201, statusText: "Created"
"
So, I have no idea why it's not sending anything to the API and I don't really know what's happening or how to fix this problem.
Any help would be greatly appreciated.
Thanks!
You should post array of the formData
const filesToSubmit = []
filesToSubmit.push((new FormData()).append('image', this.state.file))
and while posting the data the property name should be formData
axios({
url: `/api/gameMethods/playerStates/${this.props.playerId}/files/upload`,
method: 'POST',
data: {formData : filesToSubmit},
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data'
}
})
If there is an issue with constructing an array you need to add IFormFile properties to the array
So I had exactly the same issue, whereby no matter which way I twisted the FormData object and regardless of the headers I sent, I couldn't get .NET to accept the submission.
From the Node.js side, it was difficult to actually inspect the HTTP call issued by Axios - meaning I couldn't see what I was actually POSTing.
So I moved the POST to the UI for some debugging, and found that the FormData payload was not being passed as I was expecting, so it was proper that .NET was rejecting.
I ended up using the configuration below, and the POSTs began going through. Again, in my case, I thought I needed FormData, but the below was simple enough:
axios({
url: myUrl,
method: 'POST',
data: `email=${myEmail}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error);
});
Update: Not sure how encoded data will work, but since it'll be passed as a string, it's worth a shot!
const myUploadedImage = "data:image/png;name=colors.png;base64,iVBORw0KGgoAAAANSUhEUgAAA5gAAAEECAIAAADCkQz7AAAGGUlEQVR4nO3YIY/IcRzHcWd4AjZJkZQr2I1dIJlyRU5ErkJggg=="

When using mode: no-cors for a request, browser isn’t adding request header I’ve set in my frontend code

in my React app, I have the following API POST to allow the user to edit their profile (name and image).
static updateProfile(formData, user_id) {
const request = new Request(`http://localhost:4300/api/v1/profiles/${user_id}`, {
headers: new Headers({
'Authorization': getBearerToken()
}),
mode: 'no-cors',
method: "POST",
body: formData
});
return fetch(request).then(response => {
return response.json();
}).catch(error => {
return error;
});
}
The problem with the above is the header with the Authorization token is not being sent in the POST...
How can I get the Authorization header to be send in the fetch request above?
FYI, for non-multipart forms, the authorization token is sent successfully like so:
static loadProfile(user_id) {
const request = new Request(`http://localhost:4300/api/v1/profiles/${user_id}`, {
headers: new Headers({
'Authorization': getBearerToken(),
'Accept' : 'application/json',
'Content-Type' : 'application/json',
})
});
return fetch(request).then(response => {
return response.json();
}).catch(error => {
return error;
});
}
You can’t use no-cors mode if you set any special request headers, because one of effect of using it for a request is that it tells browsers to not allow your frontend JavaScript code to set any request headers other than CORS-safelisted request-headers. See the spec requirements:
To append a name/value pair to a Headers object (headers), run these steps:
Otherwise, if guard is "request-no-cors" and name/value is not a CORS-safelisted request-header, return.
In that algorithm, return equates to “return without adding that header to the Headers object”.
Authorization isn’t a CORS-safelisted request-header, so your browser won’t allow you to set if you use no-cors mode for a request. Same for Content-Type: application/json.
If the reason you’re trying to use no-cors mode is to avoid some other problem that occurs if you don’t use, the solution is to fix the underlying cause of that other problem. Because no matter what problem you might be trying to solve, no-cors mode isn’t going to turn out to be a solution in the end. It’s just going to create different problems like what you’re hitting now.
By using below code you can make a fetch request with Authorization or bearer
var url = "https://yourUrl";
var bearer = 'Bearer '+ bearer_token;
fetch(url, {
method: 'GET',
withCredentials: true,
credentials: 'include',
headers: {
'Authorization': bearer,
'X-FP-API-KEY': 'iphone',
'Content-Type': 'application/json'}
}).then((responseJson) => {
var items = JSON.parse(responseJson._bodyInit);
})
.catch(error => this.setState({
isLoading: false,
message: 'Something bad happened ' + error
}));

Categories