whatwg-fetch
Advanced tools
Comparing version 0.9.0 to 0.10.0
85
fetch.js
@@ -10,3 +10,3 @@ (function() { | ||
if (typeof name !== 'string') { | ||
name = name.toString(); | ||
name = String(name) | ||
} | ||
@@ -21,3 +21,3 @@ if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { | ||
if (typeof value !== 'string') { | ||
value = value.toString(); | ||
value = String(value) | ||
} | ||
@@ -205,18 +205,42 @@ return value | ||
function Request(url, options) { | ||
function Request(input, options) { | ||
options = options || {} | ||
this.url = url | ||
var body = options.body | ||
if (Request.prototype.isPrototypeOf(input)) { | ||
if (input.bodyUsed) { | ||
throw new TypeError('Already read') | ||
} | ||
this.url = input.url | ||
this.credentials = input.credentials | ||
if (!options.headers) { | ||
this.headers = new Headers(input.headers) | ||
} | ||
this.method = input.method | ||
this.mode = input.mode | ||
if (!body) { | ||
body = input._bodyInit | ||
input.bodyUsed = true | ||
} | ||
} else { | ||
this.url = input | ||
} | ||
this.credentials = options.credentials || 'omit' | ||
this.headers = new Headers(options.headers) | ||
this.method = normalizeMethod(options.method || 'GET') | ||
this.mode = options.mode || null | ||
this.credentials = options.credentials || this.credentials || 'omit' | ||
if (options.headers || !this.headers) { | ||
this.headers = new Headers(options.headers) | ||
} | ||
this.method = normalizeMethod(options.method || this.method || 'GET') | ||
this.mode = options.mode || this.mode || null | ||
this.referrer = null | ||
if ((this.method === 'GET' || this.method === 'HEAD') && options.body) { | ||
if ((this.method === 'GET' || this.method === 'HEAD') && body) { | ||
throw new TypeError('Body not allowed for GET or HEAD requests') | ||
} | ||
this._initBody(options.body) | ||
this._initBody(body) | ||
} | ||
Request.prototype.clone = function() { | ||
return new Request(this) | ||
} | ||
function decode(body) { | ||
@@ -256,3 +280,2 @@ var form = new FormData() | ||
this.type = 'default' | ||
this.url = null | ||
this.status = options.status | ||
@@ -267,2 +290,27 @@ this.ok = this.status >= 200 && this.status < 300 | ||
Response.prototype.clone = function() { | ||
return new Response(this._bodyInit, { | ||
status: this.status, | ||
statusText: this.statusText, | ||
headers: new Headers(this.headers), | ||
url: this.url | ||
}) | ||
} | ||
Response.error = function() { | ||
var response = new Response(null, {status: 0, statusText: ''}) | ||
response.type = 'error' | ||
return response | ||
} | ||
var redirectStatuses = [301, 302, 303, 307, 308] | ||
Response.redirect = function(url, status) { | ||
if (redirectStatuses.indexOf(status) === -1) { | ||
throw new RangeError('Invalid status code') | ||
} | ||
return new Response(null, {status: status, headers: {location: url}}) | ||
} | ||
self.Headers = Headers; | ||
@@ -273,11 +321,10 @@ self.Request = Request; | ||
self.fetch = function(input, init) { | ||
// TODO: Request constructor should accept input, init | ||
var request | ||
if (Request.prototype.isPrototypeOf(input) && !init) { | ||
request = input | ||
} else { | ||
request = new Request(input, init) | ||
} | ||
return new Promise(function(resolve, reject) { | ||
var request | ||
if (Request.prototype.isPrototypeOf(input) && !init) { | ||
request = input | ||
} else { | ||
request = new Request(input, init) | ||
} | ||
return new Promise(function(resolve, reject) { | ||
var xhr = new XMLHttpRequest() | ||
@@ -284,0 +331,0 @@ |
{ | ||
"name": "whatwg-fetch", | ||
"version": "0.9.0", | ||
"version": "0.10.0", | ||
"main": "fetch.js", | ||
@@ -5,0 +5,0 @@ "repository": "github/fetch", |
110
README.md
# window.fetch polyfill | ||
This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to uphold this code. | ||
[code-of-conduct]: http://todogroup.org/opencodeofconduct/#fetch/opensource@github.com | ||
The global `fetch` function is an easier way to make web requests and handle | ||
@@ -15,3 +18,3 @@ responses than using an XMLHttpRequest. This polyfill is written as closely as | ||
You'll also need a Promise polyfill for older browsers. | ||
You'll also need a Promise polyfill for [older browsers](http://caniuse.com/#feat=promises). | ||
@@ -28,4 +31,6 @@ ```sh | ||
(For a node.js implementation, try [node-fetch](https://github.com/bitinn/node-fetch)) | ||
For a node.js implementation, try [node-fetch](https://github.com/bitinn/node-fetch). | ||
For use with webpack, refer to [Using WebPack with shims and polyfills](http://mts.io/2015/04/08/webpack-shims-polyfills/). | ||
## Usage | ||
@@ -76,3 +81,3 @@ | ||
fetch('/query', { | ||
fetch('/users', { | ||
method: 'post', | ||
@@ -104,27 +109,42 @@ body: new FormData(form) | ||
var form = new FormData() | ||
form.append('file', input.files[0]) | ||
form.append('user', 'hubot') | ||
var data = new FormData() | ||
data.append('file', input.files[0]) | ||
data.append('user', 'hubot') | ||
fetch('/avatars', { | ||
method: 'post', | ||
body: form | ||
body: data | ||
}) | ||
``` | ||
### Success and error handlers | ||
### Caveats | ||
This causes `fetch` to behave like jQuery's `$.ajax` by rejecting the `Promise` | ||
on HTTP failure status codes like 404, 500, etc. The response `Promise` is | ||
resolved only on successful, 200 level, status codes. | ||
The `fetch` specification differs from `jQuery.ajax()` in mainly two ways that | ||
bear keeping in mind: | ||
* 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. | ||
* By default, `fetch` **won't send any cookies** to the server, resulting in | ||
unauthenticated requests if the site relies on maintaining a user session. | ||
#### Handling HTTP error statuses | ||
To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx | ||
status, define a custom response handler: | ||
```javascript | ||
function status(response) { | ||
function checkStatus(response) { | ||
if (response.status >= 200 && response.status < 300) { | ||
return response | ||
} else { | ||
var error = new Error(response.statusText) | ||
error.response = response | ||
throw error | ||
} | ||
throw new Error(response.statusText) | ||
} | ||
function json(response) { | ||
function parseJSON(response) { | ||
return response.json() | ||
@@ -134,6 +154,6 @@ } | ||
fetch('/users') | ||
.then(status) | ||
.then(json) | ||
.then(function(json) { | ||
console.log('request succeeded with json response', json) | ||
.then(checkStatus) | ||
.then(parseJSON) | ||
.then(function(data) { | ||
console.log('request succeeded with JSON response', data) | ||
}).catch(function(error) { | ||
@@ -144,18 +164,52 @@ console.log('request failed', error) | ||
### Response URL caveat | ||
#### Sending cookies | ||
The `Response` object has a URL attribute for the final responded resource. | ||
Usually this is the same as the `Request` url, but in the case of a redirect, | ||
its all transparent. Newer versions of XHR include a `responseURL` attribute | ||
that returns this value. But not every browser supports this. The compromise | ||
requires setting a special server side header to tell the browser what URL it | ||
just requested (yeah, I know browsers). | ||
To automatically send cookies for the current domain, the `credentials` option | ||
must be provided: | ||
```javascript | ||
fetch('/users', { | ||
credentials: 'same-origin' | ||
}) | ||
``` | ||
This option makes `fetch` behave similar to XMLHttpRequest with regards to | ||
cookies. Otherwise, cookies won't get sent, resulting in these requests not | ||
preserving the authentication session. | ||
Use the `include` value to send cookies in a [cross-origin resource sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) (CORS) request. | ||
```javascript | ||
fetch('https://example.com:1234/users', { | ||
credentials: 'include' | ||
}) | ||
``` | ||
#### 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`. | ||
[forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name | ||
#### Obtaining the Response URL | ||
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. | ||
``` ruby | ||
# Ruby on Rails controller example | ||
response.headers['X-Request-URL'] = request.url | ||
``` | ||
If you want `response.url` to be reliable, you'll want to set this header. The | ||
day that you ditch this polyfill and use native fetch only, you can remove the | ||
header hack. | ||
This server workaround is necessary if you need reliable `response.url` in | ||
Firefox < 32, Chrome < 37, Safari, or IE. | ||
@@ -162,0 +216,0 @@ ## Browser Support |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17057
318
215
1