2014-10-13 03:26:22 +04:00
|
|
|
# window.fetch polyfill
|
|
|
|
|
2016-11-09 17:30:22 +03:00
|
|
|
The `fetch()` function is a Promise-based mechanism for programatically making
|
|
|
|
web requests in the browser. This project is a polyfill that implements a subset
|
|
|
|
of the standard [Fetch specification][], enough to make `fetch` a viable
|
|
|
|
replacement for most uses of XMLHttpRequest in traditional web applications.
|
2015-07-08 21:02:35 +03:00
|
|
|
|
2016-11-09 17:30:22 +03:00
|
|
|
This project adheres to the [Open Code of Conduct][]. By participating, you are
|
|
|
|
expected to uphold this code.
|
2014-10-13 03:26:22 +04:00
|
|
|
|
2016-11-09 17:30:53 +03:00
|
|
|
## Table of Contents
|
|
|
|
|
|
|
|
* [Read this first](#read-this-first)
|
|
|
|
* [Installation](#installation)
|
|
|
|
* [Usage](#usage)
|
|
|
|
* [HTML](#html)
|
|
|
|
* [JSON](#json)
|
|
|
|
* [Response metadata](#response-metadata)
|
|
|
|
* [Post form](#post-form)
|
|
|
|
* [Post JSON](#post-json)
|
|
|
|
* [File upload](#file-upload)
|
|
|
|
* [Caveats](#caveats)
|
|
|
|
* [Handling HTTP error statuses](#handling-http-error-statuses)
|
|
|
|
* [Sending cookies](#sending-cookies)
|
|
|
|
* [Receiving cookies](#receiving-cookies)
|
|
|
|
* [Obtaining the Response URL](#obtaining-the-response-url)
|
|
|
|
* [Browser Support](#browser-support)
|
|
|
|
|
|
|
|
## Read this first
|
|
|
|
|
|
|
|
* If you believe you found a bug with how `fetch` behaves in Chrome or Firefox,
|
|
|
|
please **avoid opening an issue in this repository**. This project is a
|
|
|
|
_polyfill_, and since Chrome and Firefox both implement the `window.fetch`
|
|
|
|
function natively, no code from this project actually takes any effect in
|
|
|
|
these browsers. See [Browser support](#browser-support) for detailed
|
|
|
|
information.
|
|
|
|
|
|
|
|
* If you have trouble **making a request to another domain** (a different
|
|
|
|
subdomain or port number also constitutes as another domain), please
|
|
|
|
familiarize yourself with all the intricacies and limitations of [CORS][]
|
|
|
|
requests. Because CORS requires participation of the server by implementing
|
|
|
|
specific HTTP response headers, it is often nontrivial to set up or debug.
|
|
|
|
CORS is exclusively handled by the browser's internal mechanisms which this
|
|
|
|
polyfill cannot influence.
|
|
|
|
|
|
|
|
* If you have trouble **maintaining the user's session** or [CSRF][] protection
|
|
|
|
through `fetch` requests, please ensure that you've read and understood the
|
|
|
|
[Sending cookies](#sending-cookies) section.
|
|
|
|
|
|
|
|
* If this polyfill **doesn't work under Node.js environment**, that is expected,
|
|
|
|
because this project is meant for web browsers only. You should ensure that your
|
|
|
|
application doesn't try to package and run this on the server.
|
|
|
|
|
|
|
|
* If you have an idea for a new feature of `fetch`, please understand that we
|
|
|
|
are only ever going to add features and APIs that are a part of the
|
|
|
|
[Fetch specification][]. You should **submit your feature requests** to the
|
|
|
|
[repository of the specification](https://github.com/whatwg/fetch/issues)
|
|
|
|
itself, rather than this repository.
|
|
|
|
|
2014-10-13 03:26:22 +04:00
|
|
|
## Installation
|
|
|
|
|
2016-11-09 17:28:02 +03:00
|
|
|
* `npm install whatwg-fetch --save`; or
|
2014-10-13 03:26:22 +04:00
|
|
|
|
2016-11-09 17:28:02 +03:00
|
|
|
* `bower install fetch`.
|
2014-10-23 20:44:34 +04:00
|
|
|
|
2016-11-09 17:28:02 +03:00
|
|
|
You will also need a Promise polyfill for [older browsers](http://caniuse.com/#feat=promises).
|
|
|
|
We recommend [taylorhakes/promise-polyfill](https://github.com/taylorhakes/promise-polyfill)
|
|
|
|
for its small size and Promises/A+ compatibility.
|
2015-08-17 17:37:55 +03:00
|
|
|
|
2016-11-09 17:28:02 +03:00
|
|
|
For use with webpack, add this package in the `entry` configuration option
|
|
|
|
before your application entry point:
|
2016-05-19 17:36:56 +03:00
|
|
|
|
|
|
|
```javascript
|
|
|
|
entry: ['whatwg-fetch', ...]
|
|
|
|
```
|
2015-01-27 22:17:23 +03:00
|
|
|
|
2016-11-09 17:28:02 +03:00
|
|
|
For Babel and ES2015+, make sure to import the file:
|
2016-03-05 08:49:52 +03:00
|
|
|
|
|
|
|
```javascript
|
2016-11-09 17:28:02 +03:00
|
|
|
import 'whatwg-fetch'
|
2016-03-05 08:49:52 +03:00
|
|
|
```
|
|
|
|
|
2014-10-13 03:26:22 +04:00
|
|
|
## Usage
|
|
|
|
|
2016-11-09 19:31:07 +03:00
|
|
|
For a more comprehensive API reference that this polyfill supports, refer to
|
|
|
|
https://github.github.io/fetch/.
|
|
|
|
|
2014-10-13 03:26:22 +04:00
|
|
|
### HTML
|
|
|
|
|
|
|
|
```javascript
|
2014-10-14 19:57:48 +04:00
|
|
|
fetch('/users.html')
|
|
|
|
.then(function(response) {
|
|
|
|
return response.text()
|
|
|
|
}).then(function(body) {
|
|
|
|
document.body.innerHTML = body
|
|
|
|
})
|
2014-10-13 03:26:22 +04:00
|
|
|
```
|
|
|
|
|
|
|
|
### JSON
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
fetch('/users.json')
|
|
|
|
.then(function(response) {
|
|
|
|
return response.json()
|
|
|
|
}).then(function(json) {
|
|
|
|
console.log('parsed json', json)
|
|
|
|
}).catch(function(ex) {
|
|
|
|
console.log('parsing failed', ex)
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
### Response metadata
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
fetch('/users.json').then(function(response) {
|
|
|
|
console.log(response.headers.get('Content-Type'))
|
|
|
|
console.log(response.headers.get('Date'))
|
|
|
|
console.log(response.status)
|
|
|
|
console.log(response.statusText)
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
### Post form
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
var form = document.querySelector('form')
|
|
|
|
|
2015-06-23 02:12:25 +03:00
|
|
|
fetch('/users', {
|
2016-01-31 22:34:39 +03:00
|
|
|
method: 'POST',
|
2014-10-13 03:26:22 +04:00
|
|
|
body: new FormData(form)
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
### Post JSON
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
fetch('/users', {
|
2016-01-31 22:34:39 +03:00
|
|
|
method: 'POST',
|
2014-10-13 03:26:22 +04:00
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
name: 'Hubot',
|
|
|
|
login: 'hubot',
|
|
|
|
})
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
### File upload
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
var input = document.querySelector('input[type="file"]')
|
|
|
|
|
2015-06-16 00:10:15 +03:00
|
|
|
var data = new FormData()
|
|
|
|
data.append('file', input.files[0])
|
|
|
|
data.append('user', 'hubot')
|
2014-10-13 03:26:22 +04:00
|
|
|
|
|
|
|
fetch('/avatars', {
|
2016-01-31 22:34:39 +03:00
|
|
|
method: 'POST',
|
2015-06-16 00:10:15 +03:00
|
|
|
body: data
|
2014-10-13 03:26:22 +04:00
|
|
|
})
|
|
|
|
```
|
|
|
|
|
2015-06-16 00:48:14 +03:00
|
|
|
### Caveats
|
|
|
|
|
2015-06-23 02:22:41 +03:00
|
|
|
The `fetch` specification differs from `jQuery.ajax()` in mainly two ways that
|
|
|
|
bear keeping in mind:
|
2015-06-16 00:48:14 +03:00
|
|
|
|
|
|
|
* The Promise returned from `fetch()` **won't reject on HTTP error status**
|
|
|
|
even if the response is a HTTP 404 or 500. Instead, it will resolve normally,
|
|
|
|
and it will only reject on network failure, or if anything prevented the
|
|
|
|
request from completing.
|
|
|
|
|
2016-11-09 14:51:58 +03:00
|
|
|
* By default, `fetch` **won't send or receive any cookies** from the server,
|
|
|
|
resulting in unauthenticated requests if the site relies on maintaining a user
|
|
|
|
session. See [Sending cookies](#sending-cookies) for how to opt into cookie
|
|
|
|
handling.
|
2015-06-16 00:48:14 +03:00
|
|
|
|
|
|
|
#### Handling HTTP error statuses
|
2014-10-20 11:47:11 +04:00
|
|
|
|
2015-06-16 01:05:33 +03:00
|
|
|
To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx
|
|
|
|
status, define a custom response handler:
|
2014-10-20 21:43:02 +04:00
|
|
|
|
2014-10-20 11:47:11 +04:00
|
|
|
```javascript
|
2015-06-16 01:05:33 +03:00
|
|
|
function checkStatus(response) {
|
2014-10-20 21:43:41 +04:00
|
|
|
if (response.status >= 200 && response.status < 300) {
|
2015-04-14 00:17:05 +03:00
|
|
|
return response
|
2015-06-16 01:05:33 +03:00
|
|
|
} else {
|
|
|
|
var error = new Error(response.statusText)
|
|
|
|
error.response = response
|
|
|
|
throw error
|
2014-10-20 21:43:41 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-16 01:05:33 +03:00
|
|
|
function parseJSON(response) {
|
2014-10-20 21:43:41 +04:00
|
|
|
return response.json()
|
|
|
|
}
|
|
|
|
|
2014-10-20 11:47:11 +04:00
|
|
|
fetch('/users')
|
2015-06-16 01:05:33 +03:00
|
|
|
.then(checkStatus)
|
|
|
|
.then(parseJSON)
|
|
|
|
.then(function(data) {
|
|
|
|
console.log('request succeeded with JSON response', data)
|
2014-11-21 02:26:41 +03:00
|
|
|
}).catch(function(error) {
|
|
|
|
console.log('request failed', error)
|
2014-10-20 21:43:41 +04:00
|
|
|
})
|
2014-10-20 11:47:11 +04:00
|
|
|
```
|
|
|
|
|
2015-06-16 00:48:14 +03:00
|
|
|
#### Sending cookies
|
|
|
|
|
2015-06-23 02:15:38 +03:00
|
|
|
To automatically send cookies for the current domain, the `credentials` option
|
|
|
|
must be provided:
|
2015-06-16 00:48:14 +03:00
|
|
|
|
|
|
|
```javascript
|
|
|
|
fetch('/users', {
|
|
|
|
credentials: 'same-origin'
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
2016-11-09 17:30:22 +03:00
|
|
|
The "same-origin" value makes `fetch` behave similarly to XMLHttpRequest with
|
|
|
|
regards to cookies. Otherwise, cookies won't get sent, resulting in these
|
|
|
|
requests not preserving the authentication session.
|
2015-06-16 00:48:14 +03:00
|
|
|
|
2016-11-09 17:30:22 +03:00
|
|
|
For [CORS][] requests, use the "include" value to allow sending credentials to
|
|
|
|
other domains:
|
2015-09-28 19:09:28 +03:00
|
|
|
|
|
|
|
```javascript
|
|
|
|
fetch('https://example.com:1234/users', {
|
|
|
|
credentials: 'include'
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
2015-06-23 02:21:10 +03:00
|
|
|
#### Receiving cookies
|
|
|
|
|
|
|
|
Like with XMLHttpRequest, the `Set-Cookie` response header returned from the
|
|
|
|
server is a [forbidden header name][] and therefore can't be programatically
|
|
|
|
read with `response.headers.get()`. Instead, it's the browser's responsibility
|
|
|
|
to handle new cookies being set (if applicable to the current URL). Unless they
|
|
|
|
are HTTP-only, new cookies will be available through `document.cookie`.
|
|
|
|
|
2016-11-09 14:51:58 +03:00
|
|
|
Bear in mind that the default behavior of `fetch` is to ignore the `Set-Cookie`
|
|
|
|
header completely. To opt into accepting cookies from the server, you must use
|
|
|
|
the `credentials` option.
|
|
|
|
|
2015-06-16 00:48:14 +03:00
|
|
|
#### Obtaining the Response URL
|
2015-01-11 03:25:22 +03:00
|
|
|
|
2015-06-16 01:17:39 +03:00
|
|
|
Due to limitations of XMLHttpRequest, the `response.url` value might not be
|
|
|
|
reliable after HTTP redirects on older browsers.
|
|
|
|
|
|
|
|
The solution is to configure the server to set the response HTTP header
|
|
|
|
`X-Request-URL` to the current URL after any redirect that might have happened.
|
|
|
|
It should be safe to set it unconditionally.
|
2015-01-11 03:25:22 +03:00
|
|
|
|
|
|
|
``` ruby
|
2015-06-16 01:17:39 +03:00
|
|
|
# Ruby on Rails controller example
|
2015-01-11 03:25:22 +03:00
|
|
|
response.headers['X-Request-URL'] = request.url
|
|
|
|
```
|
|
|
|
|
2015-06-16 01:17:39 +03:00
|
|
|
This server workaround is necessary if you need reliable `response.url` in
|
|
|
|
Firefox < 32, Chrome < 37, Safari, or IE.
|
2015-01-11 03:25:22 +03:00
|
|
|
|
2014-10-13 03:26:22 +04:00
|
|
|
## Browser Support
|
|
|
|
|
2016-09-11 17:36:58 +03:00
|
|
|
- Chrome
|
|
|
|
- Firefox
|
|
|
|
- Safari 6.1+
|
|
|
|
- Internet Explorer 10+
|
|
|
|
|
2016-10-03 19:17:35 +03:00
|
|
|
Note: modern browsers such as Chrome, Firefox, and Microsoft Edge contain native
|
2016-11-09 17:30:22 +03:00
|
|
|
implementations of `window.fetch`, therefore the code from this polyfill doesn't
|
2016-09-25 15:15:54 +03:00
|
|
|
have any affect on those browsers. If you believe you've encountered an error
|
|
|
|
with how `window.fetch` is implemented in any of these browsers, you should file
|
|
|
|
an issue with that browser vendor instead on this project.
|
2016-11-09 17:30:22 +03:00
|
|
|
|
|
|
|
|
|
|
|
[fetch specification]: https://fetch.spec.whatwg.org
|
|
|
|
[open code of conduct]: http://todogroup.org/opencodeofconduct/#fetch/opensource@github.com
|
|
|
|
[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
|
|
|
|
"Cross-origin resource sharing"
|
2016-11-09 17:30:53 +03:00
|
|
|
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
|
|
|
|
"Cross-site request forgery"
|
2016-11-09 17:30:22 +03:00
|
|
|
[forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
|