I'm trying to use fetch api.
First i create a new Headers() object:
var oHeaders = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json',
"X-DocuSign-Authentication": '{"Username":"xxx","Password":"xxx","IntegratorKey":"xxx"}'
})
After headers is instantiated if i try to log headers everything is correct.
oHeaders.forEach(function(v){console.log(v)})
//logs: 2 application/json {"Username":"xxx","Password":"xxx","IntegratorKey":"xxx"}
the i create the Request object:
var oReq = new Request('https://eu.docusign.net/restapi/v2/login_information', {
method: 'GET',
headers: oHeaders,
mode: 'no-cors',
});
If i try to log the headers of the request object only the accept header will be there.
oReq.headers.forEach(function(v){console.log(v)})
//logs: application/json
If i try to fetch(oReq) i get 401 unauthorized response.
What makes the headers disappear?
When you set mode: 'no-cors'for a request, browsers won’t allow you to set any request headers other than CORS-safelisted request-headers. See the spec requirements:
To append a name/value (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”.
Related
According to the fetch specs it appears that as long as a Content-Type is specified that is one of "application/x-www-form-urlencoded", "multipart/form-data", or "text/plain" and other conditions are satisfied then a POST request should not result in a preflight request. In practice however I've had a difficult time specifying multiple headers for fetch in a way that doesn't cause the OPTIONS request for the preflight check.
ex 1.
fetch("https://differentsubodmain.example.com/api/resource", {
headers: {
"Content-Type": "text/plain, application/json",
Accept: "application/json"
},
method: "POST",
body: JSON.stringify({})
})
ex 2.
var myHeaders = new Headers();
myHeaders.append('Accept', 'application/json');
myHeaders.append('Content-Type', 'text/plain');
myHeaders.append('Content-Type', 'application/json');
fetch("https://differentsubodmain.example.com/api/resource", {
headers: myHeaders,
method: "POST",
body: JSON.stringify({})
})
ex 3.
fetch("https://differentsubodmain.example.com/api/resource", {
headers: [
["Content-Type", "application/json"],
["Content-Type", "text/plain"],
["Accept", "application/json"]
],
method: "POST",
body: JSON.stringify({})
})
Neither of these examples succeed in requesting without the preflight request but specifying either with only "Content-Type": "text/plain" appears to work just fine. The example here however shows both being specified in a request and suggests that it shouldn't cause a preflight. Is this just an issue with different browser implementations or am I missing something?
It looks like perhaps I hadn't read that reference carefully. Below is the important excerpt.
Warning. This intentionally does not use extract a MIME type as that algorithm is rather forgiving and servers are not expected to implement it.
If extract a MIME type were used the following request would not result in a CORS preflight and a naïve parser on the server might treat the request body as JSON
It looks like we are largely constrained to the mime types application/x-www-form-urlencoded, multipart/form-data, or text/plain to avoid preflight requests for CORS.
Reference:
https://fetch.spec.whatwg.org/#example-cors-safelisted-request-header-content-type
I'm trying to request a rest API that needs authentication with a token. When constructing the Request object, some headers disappear.
Why can't I set my Authorization header ?
let http_headers = {
"Content-type": "application/json",
'Authorization': 'Token token='+my_token,
'Accept': 'Application/json'
};
let url = this.base_url + '/api/v1/test';
let init = {
method: "POST",
headers: new Headers(http_headers),
mode: 'no-cors',
credentials: 'omit' // I try that, but it doesn't seem to have effect
};
let req = new Request( url, init );
console.log(req.headers.get("Accept")); // Application/json
console.log(req.headers.get("Authorization")); // null, why ?
See the documentation for mode
no-cors — Prevents the method from being anything other than HEAD, GET or POST, and the headers from being anything other than simple headers. If any ServiceWorkers intercept these requests, they may not add or override any headers except for those that are simple headers. In addition, JavaScript may not access any properties of the resulting Response. This ensures that ServiceWorkers do not affect the semantics of the Web and prevents security and privacy issues arising from leaking data across domains.
Set the mode to same-origin or cors to allow credentials to be set.
You probably want to use the fetch function and set the headers in the options parameter.
fetch(url, { //fetch options
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",
// Your headers here
},
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(); // parse response
Borrowed from https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
The fetch function returns a Promise that will have the Response object containing data from your api.
I am having a weird problem where when I make a fetch request from the client in development it is not sending the cookies to my server (legacy server that does not run on localhost).
Here is my code for the fetch request:
get( url ) {
return fetch(`${API_URL}${url}`, {
method: 'GET',
headers: headers(),
credentials: 'include'
}).then( parseResponse );
},
Headers is a function that returns the following object:
{
'Accept': 'application/json',
'Content-Type': 'application/json',
'mobile': 'false'
}
Here are the CORS headers I have set on the server (Access-Control-Allow-Origin is dynamic because fetch has issues with *)
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: mobile, Content-Type
Access-Control-Allow-Origin: http://localhost:3000
If I print out $_COOKIE I get back an empty array and when I look at the request I get Provisional headers are shown with no cookies.
Any ideas where I messed up?
Thanks In Advance :-)
I'm making a POST request to a node.js server and I'm having trouble getting it to work. Here's my request:
const headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'this-can-be-anything',
};
export const postVote = (id, vote) =>
fetch(`${uri}/posts/${id}`, {
method: 'POST',
headers,
body: JSON.stringify({options: vote}),
}).then(response => response.json())
.then(data => data)
.catch(err => console.log(err));
The function accepts an 'id' and a 'vote', both strings. The id is being used as part of the URI in the request, and the vote is being supplied as options so the API knows what to do with it. Both of the arguments are being passed correctly:
id = '8xf0y6ziyjabvozdd253nd'
vote = 'upVote'
Here's a link to the GitHub repository for the server/API:
Udacity Readable API
and a screenshot of the network when firing the request:
UPDATE: Added the second screenshot which shows status 200. Though it shows this and appears to have been successful, it still doesn't post to the server and the information stays the same.
What you are looking at is the OPTIONS request in the network tab. For cross origin requests, every request if preceeded by an OPTIONS request which tells the calling client (browser, for example) if those HTTP methods are supported by the remote server for use in crosss origin context.
Check the other requests out. If the OPTIONS request was responded to correctly by the server, the browser must automatically follow up with your POST request
EDIT:
Also, the docs specify the param name to be option whereas in your screenshot it is coming up as options.
Further reading: CORS
Try declaring the headers as such:
var headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'this-can-be-anything',
})
I want to send a new FormData() as the body of a POST request using the fetch api
The operation looks something like this:
var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')
fetch('https://api.myapp.com',
{
method: 'POST',
headers: {
"Content-Type": "multipart/form-data"
},
body: formData
}
)
The problem here is that the boundary, something like
boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu
never makes it into the Content-Type: header
It should look like this:
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu
When you try the "same" operation with a new XMLHttpRequest(), like so:
var request = new XMLHttpRequest()
request.open("POST", "https://api.mything.com")
request.withCredentials = true
request.send(formData)
the headers are correctly set
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu
So my questions are:
how do I make fetch behave exactly like XMLHttpRequest in this situation?
if this is not possible, why?
Thanks everybody! This community is more or less the reason I have professional success.
The solution to the problem is to explicitly set Content-Type to undefined so that your browser or whatever client you're using can set it and add that boundary value in there for you. Disappointing but true.
I removed "Content-Type" and added 'Accept' to http headers and it worked for me. Here are the headers I used,
'headers': new HttpHeaders({
// 'Content-Type': undefined,
'Accept': '*/*',
'Authorization':
"Bearer "+(JSON.parse(sessionStorage.getItem('token')).token),
'Access-Control-Allow-Origin': this.apiURL,
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
'Access-Control-Allow-Headers': 'origin,X-Requested-With,content-type,accept',
'Access-Control-Allow-Credentials': 'true'
})
fetch(url,options)
If you set a string as options.body, you have to set the Content-Type in request header ,or it will be text/plain by default.
If options.body is specific object like let a = new FormData() or let b = new URLSearchParams(), you don't have to set the Content-Type by hand.It will be added automaticlly.
for a ,it will be something like
multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
as you see, the boundary is automaticlly added.
for b, it is application/x-www-form-urlencoded;
I had the same issue, and was able to fix it by excluding the Content-Type property, allowing the browser to detect and set the boundary and content type automatically.
Your code becomes:
var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')
fetch('https://api.myapp.com',
{
method: 'POST',
body: formData
}
)
Add headers:{content-type: undefined} browser will generate a boundary for you
that is for uploading a file part-and-part with streaming
if you are adding 'multiple/form-data' it means you should create streaming and upload your file part-and-part
So it is okay to add request.headers = {content-type: undefined}
I'm using the aurelia-api (an wrapper to aurelia-fetch-client).
In this case the Content-Type default is 'application/json'. So I set the Content-Type to undefined and it worked like a charm.
According to FormData documentation, you shoudn't manually set the Content-Type header so browser itself will set it correctly:
Warning: When using FormData to submit POST requests using XMLHttpRequest or the Fetch_API with the multipart/form-data Content-Type (e.g. when uploading Files and Blobs to the server), do not explicitly set the Content-Type header on the request. Doing so will prevent the browser from being able to set the Content-Type header with the boundary expression it will use to delimit form fields in the request body.
So if your code (or library/middleware/etc) manually set the Content-Type, you have two ways to fix it:
rewrote your code (or whatever you use) to don't set Content-Type by default
set Content-Type to undefined or remove it from headers to let your browser do it's work