README.md 10.5 KB
Newer Older
D
David Graham 已提交
1 2
# window.fetch polyfill

A
Arve Knudsen 已提交
3
The `fetch()` function is a Promise-based mechanism for programmatically making
M
Mislav Marohnić 已提交
4 5 6
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.
7

8 9 10 11 12
## Table of Contents

* [Read this first](#read-this-first)
* [Installation](#installation)
* [Usage](#usage)
13
  * [Importing](#importing)
14 15 16 17 18 19 20 21 22 23
  * [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)
M
Fix ToC  
Mislav Marohnić 已提交
24
    * [Redirect modes](#redirect-modes)
25
    * [Obtaining the Response URL](#obtaining-the-response-url)
J
Jmeas 已提交
26
    * [Aborting requests](#aborting-requests)
27 28 29 30
* [Browser Support](#browser-support)

## Read this first

31 32 33 34 35 36
* If you believe you found a bug with how `fetch` behaves in your browser,
  please **don't open an issue in this repository** unless you are testing in
  an old version of a browser that doesn't support `window.fetch` natively.
  This project is a _polyfill_, and since all modern browsers now implement the
  `fetch` function natively, **no code from this project** actually takes any
  effect there. See [Browser support](#browser-support) for detailed
37 38 39
  information.

* If you have trouble **making a request to another domain** (a different
40 41 42 43 44 45
  subdomain or port number also constitutes 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.
46

47 48 49
* This project **doesn't work under Node.js environments**. It's meant for web
  browsers only. You should ensure that your application doesn't try to package
  and run this on the server.
50

51 52 53
* If you have an idea for a new feature of `fetch`, **submit your feature
  requests** to the [specification's repository](https://github.com/whatwg/fetch/issues).
  We only add features and APIs that are part of the [Fetch specification][].
54

D
David Graham 已提交
55 56
## Installation

57 58 59
```
npm install whatwg-fetch --save
```
D
David Graham 已提交
60

M
Mislav Marohnić 已提交
61 62 63 64
As an alternative to using npm, you can obtain `fetch.umd.js` from the
[Releases][] section. The UMD distribution is compatible with AMD and CommonJS
module loaders, as well as loading directly into a page via `<script>` tag.

M
Mislav Marohnić 已提交
65 66 67
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.
D
David Graham 已提交
68

69 70 71 72 73 74 75 76
## Usage

For a more comprehensive API reference that this polyfill supports, refer to
https://github.github.io/fetch/.

### Importing

Importing will automatically polyfill `window.fetch` and related APIs:
T
Tarik Ansari 已提交
77 78

```javascript
79 80 81
import 'whatwg-fetch'

window.fetch(...)
T
Tarik Ansari 已提交
82
```
D
David Frank 已提交
83

84 85
If for some reason you need to access the polyfill implementation, it is
available via exports:
J
Jamon Holmgren 已提交
86 87

```javascript
88 89 90 91
import {fetch as fetchPolyfill} from 'whatwg-fetch'

window.fetch(...)   // use native browser version
fetchPolyfill(...)  // use polyfill implementation
J
Jamon Holmgren 已提交
92 93
```

94 95 96
This approach can be used to, for example, use [abort
functionality](#aborting-requests) in browsers that implement a native but
outdated version of fetch that doesn't support aborting.
D
David Graham 已提交
97

98 99 100 101 102 103
For use with webpack, add this package in the `entry` configuration option
before your application entry point:

```javascript
entry: ['whatwg-fetch', ...]
```
104

D
David Graham 已提交
105 106 107
### HTML

```javascript
D
David Graham 已提交
108 109 110 111 112 113
fetch('/users.html')
  .then(function(response) {
    return response.text()
  }).then(function(body) {
    document.body.innerHTML = body
  })
D
David Graham 已提交
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
```

### 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')

M
Mislav Marohnić 已提交
145
fetch('/users', {
J
Jmeas 已提交
146
  method: 'POST',
D
David Graham 已提交
147 148 149 150 151 152 153 154
  body: new FormData(form)
})
```

### Post JSON

```javascript
fetch('/users', {
J
Jmeas 已提交
155
  method: 'POST',
D
David Graham 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})
```

### File upload

```javascript
var input = document.querySelector('input[type="file"]')

171 172 173
var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')
D
David Graham 已提交
174 175

fetch('/avatars', {
J
Jmeas 已提交
176
  method: 'POST',
177
  body: data
D
David Graham 已提交
178 179 180
})
```

181 182 183
### Caveats

* The Promise returned from `fetch()` **won't reject on HTTP error status**
A
Arve Knudsen 已提交
184 185
  even if the response is an HTTP 404 or 500. Instead, it will resolve normally,
  and it will only reject on network failure or if anything prevented the
186 187
  request from completing.

188 189 190 191
* For maximum browser compatibility when it comes to sending & receiving
  cookies, always supply the `credentials: 'same-origin'` option instead of
  relying on the default. See [Sending cookies](#sending-cookies).

M
Mislav Marohnić 已提交
192 193 194 195
* Not all Fetch standard options are supported in this polyfill. For instance,
  [`redirect`](#redirect-modes) and
  [`cache`](https://github.github.io/fetch/#caveats) directives are ignored.

196
#### Handling HTTP error statuses
197

198 199
To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx
status, define a custom response handler:
200

201
```javascript
202
function checkStatus(response) {
203
  if (response.status >= 200 && response.status < 300) {
204
    return response
205 206 207 208
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
209 210 211
  }
}

212
function parseJSON(response) {
213 214 215
  return response.json()
}

216
fetch('/users')
217 218 219 220
  .then(checkStatus)
  .then(parseJSON)
  .then(function(data) {
    console.log('request succeeded with JSON response', data)
221 222
  }).catch(function(error) {
    console.log('request failed', error)
223
  })
224 225
```

226 227
#### Sending cookies

228 229
For [CORS][] requests, use `credentials: 'include'` to allow sending credentials
to other domains:
230 231

```javascript
232 233
fetch('https://example.com:1234/users', {
  credentials: 'include'
234 235 236
})
```

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
The default value for `credentials` is "same-origin".

The default for `credentials` wasn't always the same, though. The following
versions of browsers implemented an older version of the fetch specification
where the default was "omit":

* Firefox 39-60
* Chrome 42-67
* Safari 10.1-11.1.2

If you target these browsers, it's advisable to always specify `credentials:
'same-origin'` explicitly with all fetch requests instead of relying on the
default:

```javascript
fetch('/users', {
  credentials: 'same-origin'
})
```
256

M
Mislav Marohnić 已提交
257 258 259 260 261 262
Note: due to [limitations of
XMLHttpRequest](https://github.com/github/fetch/pull/56#issuecomment-68835992),
using `credentials: 'omit'` is not respected for same domains in browsers where
this polyfill is active. Cookies will always be sent to same domains in older
browsers.

263 264
#### Receiving cookies

A
Arve Knudsen 已提交
265 266
As with XMLHttpRequest, the `Set-Cookie` response header returned from the
server is a [forbidden header name][] and therefore can't be programmatically
267 268 269 270
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`.

M
Mislav Marohnić 已提交
271 272 273 274 275 276 277 278 279
#### Redirect modes

The Fetch specification defines these values for [the `redirect`
option](https://fetch.spec.whatwg.org/#concept-request-redirect-mode): "follow"
(the default), "error", and "manual".

Due to limitations of XMLHttpRequest, only the "follow" mode is available in
browsers where this polyfill is active.

280
#### Obtaining the Response URL
J
Joshua Peek 已提交
281

282 283 284 285 286 287
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.
J
Joshua Peek 已提交
288 289

``` ruby
290
# Ruby on Rails controller example
J
Joshua Peek 已提交
291 292 293
response.headers['X-Request-URL'] = request.url
```

294 295
This server workaround is necessary if you need reliable `response.url` in
Firefox < 32, Chrome < 37, Safari, or IE.
J
Joshua Peek 已提交
296

J
Jmeas 已提交
297 298 299 300 301
#### Aborting requests

This polyfill supports
[the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch).
However, aborting a fetch requires use of two additional DOM APIs:
302
[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and
J
Jmeas 已提交
303 304
[AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
Typically, browsers that do not support fetch will also not support
305 306 307
AbortController or AbortSignal. Consequently, you will need to include
[an additional polyfill](https://github.com/mo/abortcontroller-polyfill#readme)
for these APIs to abort fetches:
J
Jmeas 已提交
308 309

```js
310 311 312
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
import {fetch} from 'whatwg-fetch'

313 314 315
// use native browser implementation if it supports aborting
const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch

J
Jmeas 已提交
316 317
const controller = new AbortController()

318
abortableFetch('/avatars', {
J
Jmeas 已提交
319
  signal: controller.signal
J
James Smith 已提交
320 321 322 323
}).catch(function(ex) {
  if (ex.name === 'AbortError') {
    console.log('request aborted')
  }
J
Jmeas 已提交
324 325 326
})

// some time later...
327
controller.abort()
J
Jmeas 已提交
328 329
```

D
David Graham 已提交
330 331
## Browser Support

332 333 334 335 336
- Chrome
- Firefox
- Safari 6.1+
- Internet Explorer 10+

R
Roney Thomas 已提交
337
Note: modern browsers such as Chrome, Firefox, Microsoft Edge, and Safari contain native
M
Mislav Marohnić 已提交
338
implementations of `window.fetch`, therefore the code from this polyfill doesn't
C
Chris Angelico 已提交
339
have any effect on those browsers. If you believe you've encountered an error
340
with how `window.fetch` is implemented in any of these browsers, you should file
V
Vagish Vela 已提交
341
an issue with that browser vendor instead of this project.
M
Mislav Marohnić 已提交
342 343 344 345 346


  [fetch specification]: https://fetch.spec.whatwg.org
  [cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
    "Cross-origin resource sharing"
347 348
  [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
    "Cross-site request forgery"
M
Mislav Marohnić 已提交
349
  [forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
M
Mislav Marohnić 已提交
350
  [releases]: https://github.com/github/fetch/releases