I have this request function that is wrappers around the fetch API to issue request to my API. But when my frontend app issues request, the headers object is always empty. What am I doing wrong ?
export function request(method, url, payload) {
const body = JSON.stringify(payload);
const headers = new Headers();
headers.append('Content-Type', 'application/json');
const parameters = {
headers: headers,
method: method,
body: body,
cache: "default"
};
return Observable.create(observer => {
fetch(url, parameters)
.then(response => {
observer.next(response);
observer.complete();
})
.catch(error => {
observer.error(error);
});
});
}
Have you checked in the network tab of the DevTools if the headers are really missing?
I have the same trouble than you describe, my Header object seems always empty from the Chrome DevTools, but if try to check a specific header like
let headers = new Headers({'X-whatever-name': 'whatever-value'});
myHeader.has('X-whatever-name'); // returns true
Also if I check the detail of the Request Headers in the DevTools (Networking tab), I can see that my custom headers are sent properly.
So only the JS api (entries, keys, ...) seems to be broken for me, but the request is correctly sent.
Try just making it a simple object:
const headers = {
"Content-Type": "application/json"
};
Related
I want to set _boundry in my header.
First, I dispatch the form data:
//component.js
const form = new FormData();
form.append('email', 'eray#serviceUser.com')
form.append('password', '12121212')
dispatch(FetchLogin.action(form))
Second, I prepare api call;
//loginService.js
import api from '#/Services'
export default async form => {
const response = await api.post('user/login/', form)
return response.data
}
Third, I make api call;
//Services/index.js
import axios from 'axios'
import { Config } from '#/Config'
const instance = axios.create({
baseURL: Config.API_URL,
headers: {
'Content-Type': `multipart/form-data; boundary=${form._boundary}`, //Cannot access form here
},
timeout: 3000,
})
instance.interceptors.response.use(
response => response,
({ message, response: { data, status } }) => {
return handleError({ message, data, status })
},
)
export default instance
I want to access form data within to axios instance to be able to use form._boundry in headers.
How can I pass form data from loginService.js to Services/index.js?
This question seems to come up often enough yet I cannot seem to find a canonical answer so here goes...
When performing AJAX requests from a browser (via fetch or XMLHttpRequest), the runtime knows what to do for certain request body formats and will automatically set the appropriate Content-type header
If the request body is a FormData instance, the Content-type will be set to multipart/form-data and will also include the appropriate mime boundary tokens from the data instance.
All of these examples will post the data as multipart/form-data with appropriate mime boundary tokens
const body = new FormData();
// attach files and other fields
body.append("file", fileInput.files[0]);
body.append("foo", "foo");
body.append("bar", "bar");
// fetch
fetch(url, { method: "POST", body });
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.send(body);
// Axios
axios.post(url, body);
If the request body is a URLSearchParams instance, the Content-type will be set to application/x-www-form-urlencoded
All of these examples will post the data as application/x-www-form-urlencoded
const body = new URLSearchParams({ foo: "foo", bar: "bar" });
// serialises to "foo=foo&bar=bar"
// fetch
fetch(url, { method: "POST", body });
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.send(body);
// Axios
axios.post(url, body);
You only need to manually set the content-type if you intend to send string data in a particular format, eg text/xml, application/json, etc since the runtime cannot infer the type from the data.
const body = JSON.stringify({ foo: "foo", bar: "bar" });
// fetch
fetch(url, {
method: "POST",
headers: {
"content-type": "application/json",
},
body
});
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.setRequestHeader("content-type", "application/json");
xhr.send(body);
On Axios
Axios will automatically stringify JavaScript data structures passed into the data parameter and set the Content-type header to application/json so you only need minimal configuration when dealing with JSON APIs
// no extra headers, no JSON.stringify()
axios.post(url, { foo: "foo", bar: "bar" })
Under the hood, Axios uses XMLHttpRequest so the specifications for FormData and URLSearchParams also apply.
Axios v0.27.1 is broken
This specific version of Axios is unable to make a proper request with FormData. Do not use it!
Axios v1.0.0+ is broken
This is verging into my own opinion but every Axios release post v1.0.0 has been fundamentally broken in one way or another. I simply cannot recommend anyone use it for any reason.
Much better alternatives are:
The Fetch API (also available in Node v18+)
got for Node.js
ky for browsers
NodeJS
When using Axios from the backend, it will not infer Content-type headers from FormData instances. You can work around this using a request interceptor.
axios.interceptors.request.use(config => {
if (config.data instanceof FormData) {
Object.assign(config.headers, config.data.getHeaders());
}
return config;
}, null, { synchronous: true });
or simply merge in the headers when making a request
axios.post(url, body, {
headers: {
"X-Any-Other-Headers": "value",
...body.getHeaders(),
},
});
See https://github.com/axios/axios#form-data
On jQuery $.ajax()
jQuery's $.ajax() method (and convenience methods like $.post()) default to sending request body payloads as application/x-www-form-urlencoded. JavaScript data structures will be automatically serialised using jQuery.param() unless told not to. If you want the browser to automatically set the Content-type header based on the body format, you also need to configure that in the options
const body = new FormData()
body.append("foo", "foo")
body.append("bar", "bar")
$.ajax({
url,
method: "POST",
data: body,
contentType: false, // let the browser figure it out
processData: false // don't attempt to serialise data
})
I want to set _boundry in my header.
First, I dispatch the form data:
//component.js
const form = new FormData();
form.append('email', 'eray#serviceUser.com')
form.append('password', '12121212')
dispatch(FetchLogin.action(form))
Second, I prepare api call;
//loginService.js
import api from '#/Services'
export default async form => {
const response = await api.post('user/login/', form)
return response.data
}
Third, I make api call;
//Services/index.js
import axios from 'axios'
import { Config } from '#/Config'
const instance = axios.create({
baseURL: Config.API_URL,
headers: {
'Content-Type': `multipart/form-data; boundary=${form._boundary}`, //Cannot access form here
},
timeout: 3000,
})
instance.interceptors.response.use(
response => response,
({ message, response: { data, status } }) => {
return handleError({ message, data, status })
},
)
export default instance
I want to access form data within to axios instance to be able to use form._boundry in headers.
How can I pass form data from loginService.js to Services/index.js?
This question seems to come up often enough yet I cannot seem to find a canonical answer so here goes...
When performing AJAX requests from a browser (via fetch or XMLHttpRequest), the runtime knows what to do for certain request body formats and will automatically set the appropriate Content-type header
If the request body is a FormData instance, the Content-type will be set to multipart/form-data and will also include the appropriate mime boundary tokens from the data instance.
All of these examples will post the data as multipart/form-data with appropriate mime boundary tokens
const body = new FormData();
// attach files and other fields
body.append("file", fileInput.files[0]);
body.append("foo", "foo");
body.append("bar", "bar");
// fetch
fetch(url, { method: "POST", body });
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.send(body);
// Axios
axios.post(url, body);
If the request body is a URLSearchParams instance, the Content-type will be set to application/x-www-form-urlencoded
All of these examples will post the data as application/x-www-form-urlencoded
const body = new URLSearchParams({ foo: "foo", bar: "bar" });
// serialises to "foo=foo&bar=bar"
// fetch
fetch(url, { method: "POST", body });
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.send(body);
// Axios
axios.post(url, body);
You only need to manually set the content-type if you intend to send string data in a particular format, eg text/xml, application/json, etc since the runtime cannot infer the type from the data.
const body = JSON.stringify({ foo: "foo", bar: "bar" });
// fetch
fetch(url, {
method: "POST",
headers: {
"content-type": "application/json",
},
body
});
// XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.setRequestHeader("content-type", "application/json");
xhr.send(body);
On Axios
Axios will automatically stringify JavaScript data structures passed into the data parameter and set the Content-type header to application/json so you only need minimal configuration when dealing with JSON APIs
// no extra headers, no JSON.stringify()
axios.post(url, { foo: "foo", bar: "bar" })
Under the hood, Axios uses XMLHttpRequest so the specifications for FormData and URLSearchParams also apply.
Axios v0.27.1 is broken
This specific version of Axios is unable to make a proper request with FormData. Do not use it!
Axios v1.0.0+ is broken
This is verging into my own opinion but every Axios release post v1.0.0 has been fundamentally broken in one way or another. I simply cannot recommend anyone use it for any reason.
Much better alternatives are:
The Fetch API (also available in Node v18+)
got for Node.js
ky for browsers
NodeJS
When using Axios from the backend, it will not infer Content-type headers from FormData instances. You can work around this using a request interceptor.
axios.interceptors.request.use(config => {
if (config.data instanceof FormData) {
Object.assign(config.headers, config.data.getHeaders());
}
return config;
}, null, { synchronous: true });
or simply merge in the headers when making a request
axios.post(url, body, {
headers: {
"X-Any-Other-Headers": "value",
...body.getHeaders(),
},
});
See https://github.com/axios/axios#form-data
On jQuery $.ajax()
jQuery's $.ajax() method (and convenience methods like $.post()) default to sending request body payloads as application/x-www-form-urlencoded. JavaScript data structures will be automatically serialised using jQuery.param() unless told not to. If you want the browser to automatically set the Content-type header based on the body format, you also need to configure that in the options
const body = new FormData()
body.append("foo", "foo")
body.append("bar", "bar")
$.ajax({
url,
method: "POST",
data: body,
contentType: false, // let the browser figure it out
processData: false // don't attempt to serialise data
})
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.
I try to get a fresh list of data by invoking a url using fetch. This url return a json. Following the instructions described in these links fetch(), how do you make a non-cached request? and https://hacks.mozilla.org/2016/03/referrer-and-cache-control-apis-for-fetch/, I build the following init object.
const headers = new Headers();
headers.append('pragma', 'no-cache');
headers.append('cache-control', 'no-cache');
const init = {
method: 'GET',
headers: headers,
cache: 'reload',
};
But after adding it as a second argument to fetch method the return data does not include the most recently added data, so I think it is due to cache.
The complete example is as follows
function getYears() {
const id = getId();
const request = new Request(`index.php?ep=name&id=${id}`);
const headers = new Headers();
headers.append('pragma', 'no-cache');
headers.append('cache-control', 'no-cache');
const init = {
method: 'GET',
headers: headers,
cache: 'reload',
};
fetch(request, init)
.then(response => response.json())
.then(json => {
window.yearCollection = years;
})
.catch(error => {
console.log(error.message);
});
}
The logical flow is the following, when loading the page the method getYears () is invoked, then when sending data in a form, the page is reloaded. Here the list of years is not updated, I must refresh the page manually, to get the list with the updated data
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