mirror of
https://gitlab.com/news-flash/article_scraper.git
synced 2025-07-07 16:15:32 +02:00
fixes
This commit is contained in:
parent
d8e3a75b01
commit
0834c4d72a
8 changed files with 3234 additions and 489 deletions
|
@ -1,40 +1,35 @@
|
||||||
<article><DIV id="readability-page-1" class="page"><section>
|
<article><DIV id="readability-page-1" class="page"><section><p><strong>So finally you're <a href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/" target="_blank">testing your frontend JavaScript code</a>? Great! The more you
|
||||||
<p><strong>So finally you're <a href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/" target="_blank">testing your frontend JavaScript code</a>? Great! The more you
|
|
||||||
write tests, the more confident you are with your code… but how much precisely?
|
write tests, the more confident you are with your code… but how much precisely?
|
||||||
That's where <a href="http://en.wikipedia.org/wiki/Code_coverage" target="_blank">code coverage</a> might
|
That's where <a href="http://en.wikipedia.org/wiki/Code_coverage" target="_blank">code coverage</a> might
|
||||||
help.</strong>
|
help.</strong></p>
|
||||||
</p>
|
<p>The idea behind code coverage is to record which parts of your code (functions,
|
||||||
<p>The idea behind code coverage is to record which parts of your code (functions,
|
|
||||||
statements, conditionals and so on) have been executed by your test suite,
|
statements, conditionals and so on) have been executed by your test suite,
|
||||||
to compute metrics out of these data and usually to provide tools for navigating
|
to compute metrics out of these data and usually to provide tools for navigating
|
||||||
and inspecting them.</p>
|
and inspecting them.</p>
|
||||||
<p>Not a lot of frontend developers I know actually test their frontend code,
|
<p>Not a lot of frontend developers I know actually test their frontend code,
|
||||||
and I can barely imagine how many of them have ever setup code coverage…
|
and I can barely imagine how many of them have ever setup code coverage…
|
||||||
Mostly because there are not many frontend-oriented tools in this area
|
Mostly because there are not many frontend-oriented tools in this area
|
||||||
I guess.</p>
|
I guess.</p>
|
||||||
<p>Actually I've only found one which provides an adapter for <a href="http://visionmedia.github.io/mocha/" target="_blank">Mocha</a> and
|
<p>Actually I've only found one which provides an adapter for <a href="http://visionmedia.github.io/mocha/" target="_blank">Mocha</a> and
|
||||||
actually works…</p>
|
actually works…</p>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>Drinking game for web devs:
|
<p>Drinking game for web devs:
|
||||||
<br>(1) Think of a noun
|
<br>(1) Think of a noun
|
||||||
<br>(2) Google "<noun>.js"
|
<br>(2) Google "<noun>.js"
|
||||||
<br>(3) If a library with that name exists - drink</p>— Shay Friedman (@ironshay)
|
<br>(3) If a library with that name exists - drink</p>— Shay Friedman (@ironshay)
|
||||||
<a href="https://twitter.com/ironshay/statuses/370525864523743232" target="_blank">August 22, 2013</a>
|
<a href="https://twitter.com/ironshay/statuses/370525864523743232" target="_blank">August 22, 2013</a>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<p><strong><a href="http://blanketjs.org/" target="_blank">Blanket.js</a></strong> is an <em>easy to install, easy to configure,
|
<p><strong><a href="http://blanketjs.org/" target="_blank">Blanket.js</a></strong> is an <em>easy to install, easy to configure,
|
||||||
and easy to use JavaScript code coverage library that works both in-browser and
|
and easy to use JavaScript code coverage library that works both in-browser and
|
||||||
with nodejs.</em>
|
with nodejs.</em></p>
|
||||||
</p>
|
<p>Its use is dead easy, adding Blanket support to your Mocha test suite
|
||||||
<p>Its use is dead easy, adding Blanket support to your Mocha test suite
|
|
||||||
is just matter of adding this simple line to your HTML test file:</p>
|
is just matter of adding this simple line to your HTML test file:</p>
|
||||||
<pre><code><script src="vendor/blanket.js"
|
<pre><code><script src="vendor/blanket.js"
|
||||||
data-cover-adapter="vendor/mocha-blanket.js"></script>
|
data-cover-adapter="vendor/mocha-blanket.js"></script>
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<p>Source files: <a href="https://raw.github.com/alex-seville/blanket/master/dist/qunit/blanket.min.js" target="_blank">blanket.js</a>,
|
||||||
<p>Source files: <a href="https://raw.github.com/alex-seville/blanket/master/dist/qunit/blanket.min.js" target="_blank">blanket.js</a>,
|
<a href="https://raw.github.com/alex-seville/blanket/master/src/adapters/mocha-blanket.js" target="_blank">mocha-blanket.js</a></p>
|
||||||
<a href="https://raw.github.com/alex-seville/blanket/master/src/adapters/mocha-blanket.js" target="_blank">mocha-blanket.js</a>
|
<p>As an example, let's reuse the silly <code>Cow</code> example we used
|
||||||
</p>
|
|
||||||
<p>As an example, let's reuse the silly <code>Cow</code> example we used
|
|
||||||
<a href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/" target="_blank">in a previous episode</a>:</p>
|
<a href="/code/2013/testing-frontend-javascript-code-using-mocha-chai-and-sinon/" target="_blank">in a previous episode</a>:</p>
|
||||||
<pre><code>// cow.js
|
<pre><code>// cow.js
|
||||||
(function(exports) {
|
(function(exports) {
|
||||||
|
@ -54,8 +49,7 @@ with nodejs.</em>
|
||||||
};
|
};
|
||||||
})(this);
|
})(this);
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<p>And its test suite, powered by Mocha and <a href="http://chaijs.com/" target="_blank">Chai</a>:</p>
|
||||||
<p>And its test suite, powered by Mocha and <a href="http://chaijs.com/" target="_blank">Chai</a>:</p>
|
|
||||||
<pre><code>var expect = chai.expect;
|
<pre><code>var expect = chai.expect;
|
||||||
|
|
||||||
describe("Cow", function() {
|
describe("Cow", function() {
|
||||||
|
@ -79,8 +73,7 @@ describe("Cow", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<p>Let's create the HTML test file for it, featuring Blanket and its adapter
|
||||||
<p>Let's create the HTML test file for it, featuring Blanket and its adapter
|
|
||||||
for Mocha:</p>
|
for Mocha:</p>
|
||||||
<pre><code><!DOCTYPE html>
|
<pre><code><!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
@ -104,29 +97,24 @@ describe("Cow", function() {
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<p><strong>Notes</strong>:</p>
|
||||||
<p><strong>Notes</strong>:</p>
|
<ul>
|
||||||
<ul>
|
<li>Notice the <code>data-cover</code> attribute we added to the script tag
|
||||||
<li>Notice the <code>data-cover</code> attribute we added to the script tag
|
|
||||||
loading the source of our library;</li>
|
loading the source of our library;</li>
|
||||||
<li>The HTML test file <em>must</em> be served over HTTP for the adapter to
|
<li>The HTML test file <em>must</em> be served over HTTP for the adapter to
|
||||||
be loaded.</li>
|
be loaded.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Running the tests now gives us something like this:</p>
|
<p>Running the tests now gives us something like this:</p>
|
||||||
<p>
|
<p><img alt="screenshot" src="/static/code/2013/blanket-coverage.png"></p>
|
||||||
<img alt="screenshot" src="/static/code/2013/blanket-coverage.png">
|
<p>As you can see, the report at the bottom highlights that we haven't actually
|
||||||
</p>
|
|
||||||
<p>As you can see, the report at the bottom highlights that we haven't actually
|
|
||||||
tested the case where an error is raised in case a target name is missing.
|
tested the case where an error is raised in case a target name is missing.
|
||||||
We've been informed of that, nothing more, nothing less. We simply know
|
We've been informed of that, nothing more, nothing less. We simply know
|
||||||
we're missing a test here. Isn't this cool? I think so!</p>
|
we're missing a test here. Isn't this cool? I think so!</p>
|
||||||
<p>Just remember that code coverage will only <a href="http://codebetter.com/karlseguin/2008/12/09/code-coverage-use-it-wisely/" target="_blank">bring you numbers</a> and
|
<p>Just remember that code coverage will only <a href="http://codebetter.com/karlseguin/2008/12/09/code-coverage-use-it-wisely/" target="_blank">bring you numbers</a> and
|
||||||
raw information, not actual proofs that the whole of your <em>code logic</em> has
|
raw information, not actual proofs that the whole of your <em>code logic</em> has
|
||||||
been actually covered. If you ask me, the best inputs you can get about
|
been actually covered. If you ask me, the best inputs you can get about
|
||||||
your code logic and implementation ever are the ones issued out of <a href="http://www.extremeprogramming.org/rules/pair.html" target="_blank">pair programming</a>
|
your code logic and implementation ever are the ones issued out of <a href="http://www.extremeprogramming.org/rules/pair.html" target="_blank">pair programming</a>
|
||||||
sessions
|
sessions
|
||||||
and <a href="http://alexgaynor.net/2013/sep/26/effective-code-review/" target="_blank">code reviews</a> —
|
and <a href="http://alexgaynor.net/2013/sep/26/effective-code-review/" target="_blank">code reviews</a> —
|
||||||
but that's another story.</p>
|
but that's another story.</p>
|
||||||
<p><strong>So is code coverage silver bullet? No. Is it useful? Definitely. Happy testing!</strong>
|
<p><strong>So is code coverage silver bullet? No. Is it useful? Definitely. Happy testing!</strong></p></section></DIV></article>
|
||||||
</p>
|
|
||||||
</section></DIV></article>
|
|
||||||
|
|
|
@ -1,284 +1,144 @@
|
||||||
<article><DIV id="readability-page-1" class="page">
|
<article><DIV id="readability-page-1" class="page"><article role="article"><p>For more than a decade the Web has used XMLHttpRequest (XHR) to achieve
|
||||||
<article role="article">
|
|
||||||
<p>For more than a decade the Web has used XMLHttpRequest (XHR) to achieve
|
|
||||||
asynchronous requests in JavaScript. While very useful, XHR is not a very
|
asynchronous requests in JavaScript. While very useful, XHR is not a very
|
||||||
nice API. It suffers from lack of separation of concerns. The input, output
|
nice API. It suffers from lack of separation of concerns. The input, output
|
||||||
and state are all managed by interacting with one object, and state is
|
and state are all managed by interacting with one object, and state is
|
||||||
tracked using events. Also, the event-based model doesn’t play well with
|
tracked using events. Also, the event-based model doesn’t play well with
|
||||||
JavaScript’s recent focus on Promise- and generator-based asynchronous
|
JavaScript’s recent focus on Promise- and generator-based asynchronous
|
||||||
programming.</p>
|
programming.</p>
|
||||||
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" target="_blank">Fetch API</a> intends
|
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" target="_blank">Fetch API</a> intends
|
||||||
to fix most of these problems. It does this by introducing the same primitives
|
to fix most of these problems. It does this by introducing the same primitives
|
||||||
to JS that are used in the HTTP protocol. In addition, it introduces a
|
to JS that are used in the HTTP protocol. In addition, it introduces a
|
||||||
utility function <code>fetch()</code> that succinctly captures the intention
|
utility function <code>fetch()</code> that succinctly captures the intention
|
||||||
of retrieving a resource from the network.</p>
|
of retrieving a resource from the network.</p>
|
||||||
<p>The <a href="https://fetch.spec.whatwg.org" target="_blank">Fetch specification</a>, which
|
<p>The <a href="https://fetch.spec.whatwg.org" target="_blank">Fetch specification</a>, which
|
||||||
defines the API, nails down the semantics of a user agent fetching a resource.
|
defines the API, nails down the semantics of a user agent fetching a resource.
|
||||||
This, combined with ServiceWorkers, is an attempt to:</p>
|
This, combined with ServiceWorkers, is an attempt to:</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Improve the offline experience.</li>
|
<li>Improve the offline experience.</li>
|
||||||
<li>Expose the building blocks of the Web to the platform as part of the
|
<li>Expose the building blocks of the Web to the platform as part of the
|
||||||
<a href="https://extensiblewebmanifesto.org/" target="_blank">extensible web movement</a>.</li>
|
<a href="https://extensiblewebmanifesto.org/" target="_blank">extensible web movement</a>.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>As of this writing, the Fetch API is available in Firefox 39 (currently
|
<p>As of this writing, the Fetch API is available in Firefox 39 (currently
|
||||||
Nightly) and Chrome 42 (currently dev). Github has a <a href="https://github.com/github/fetch" target="_blank">Fetch polyfill</a>.</p>
|
Nightly) and Chrome 42 (currently dev). Github has a <a href="https://github.com/github/fetch" target="_blank">Fetch polyfill</a>.</p>
|
||||||
|
|
||||||
<h2>Feature detection</h2>
|
<h2>Feature detection</h2>
|
||||||
|
<p>Fetch API support can be detected by checking for <code>Headers</code>,<code>Request</code>, <code>Response</code> or <code>fetch</code> on
|
||||||
<p>Fetch API support can be detected by checking for <code>Headers</code>,<code>Request</code>, <code>Response</code> or <code>fetch</code> on
|
|
||||||
the <code>window</code> or <code>worker</code> scope.</p>
|
the <code>window</code> or <code>worker</code> scope.</p>
|
||||||
|
|
||||||
<h2>Simple fetching</h2>
|
<h2>Simple fetching</h2>
|
||||||
|
<p>The most useful, high-level part of the Fetch API is the <code>fetch()</code> function.
|
||||||
<p>The most useful, high-level part of the Fetch API is the <code>fetch()</code> function.
|
|
||||||
In its simplest form it takes a URL and returns a promise that resolves
|
In its simplest form it takes a URL and returns a promise that resolves
|
||||||
to the response. The response is captured as a <code>Response</code> object.</p>
|
to the response. The response is captured as a <code>Response</code> object.</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre>fetch<span>(</span><span>"/data.json"</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>res<span>)</span><span>{</span><span>// res instanceof Response == true.</span><span>if</span><span>(</span>res.<span>ok</span><span>)</span><span>{</span>
|
||||||
<table>
|
res.<span>json</span><span>(</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>data<span>)</span><span>{</span>
|
||||||
<tbody>
|
console.<span>log</span><span>(</span>data.<span>entries</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span><span>}</span><span>else</span><span>{</span>
|
||||||
<tr>
|
console.<span>log</span><span>(</span><span>"Looks like the response wasn't perfect, got status"</span><span>,</span> res.<span>status</span><span>)</span><span>;</span><span>}</span><span>}</span><span>,</span><span>function</span><span>(</span>e<span>)</span><span>{</span>
|
||||||
<td>
|
console.<span>log</span><span>(</span><span>"Fetch failed!"</span><span>,</span> e<span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<pre>fetch<span>(</span><span>"/data.json"</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>res<span>)</span> <span>{</span>
|
<p>Submitting some parameters, it would look like this:</p>
|
||||||
<span>// res instanceof Response == true.</span>
|
<div><table><tbody><tr><td><pre>fetch<span>(</span><span>"http://www.example.org/submit.php"</span><span>,</span><span>{</span>
|
||||||
<span>if</span> <span>(</span>res.<span>ok</span><span>)</span> <span>{</span>
|
method<span>:</span><span>"POST"</span><span>,</span>
|
||||||
res.<span>json</span><span>(</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>data<span>)</span> <span>{</span>
|
headers<span>:</span><span>{</span><span>"Content-Type"</span><span>:</span><span>"application/x-www-form-urlencoded"</span><span>}</span><span>,</span>
|
||||||
console.<span>log</span><span>(</span>data.<span>entries</span><span>)</span><span>;</span>
|
body<span>:</span><span>"firstName=Nikhil&favColor=blue&password=easytoguess"</span><span>}</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>res<span>)</span><span>{</span><span>if</span><span>(</span>res.<span>ok</span><span>)</span><span>{</span>
|
||||||
<span>}</span><span>)</span><span>;</span>
|
alert<span>(</span><span>"Perfect! Your settings are saved."</span><span>)</span><span>;</span><span>}</span><span>else</span><span>if</span><span>(</span>res.<span>status</span><span>==</span><span>401</span><span>)</span><span>{</span>
|
||||||
<span>}</span> <span>else</span> <span>{</span>
|
alert<span>(</span><span>"Oops! You are not authorized."</span><span>)</span><span>;</span><span>}</span><span>}</span><span>,</span><span>function</span><span>(</span>e<span>)</span><span>{</span>
|
||||||
console.<span>log</span><span>(</span><span>"Looks like the response wasn't perfect, got status"</span><span>,</span> res.<span>status</span><span>)</span><span>;</span>
|
alert<span>(</span><span>"Error submitting form!"</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<span>}</span>
|
<p>The <code>fetch()</code> function’s arguments are the same as those passed
|
||||||
<span>}</span><span>,</span> <span>function</span><span>(</span>e<span>)</span> <span>{</span>
|
|
||||||
console.<span>log</span><span>(</span><span>"Fetch failed!"</span><span>,</span> e<span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>Submitting some parameters, it would look like this:</p>
|
|
||||||
<P>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre>fetch<span>(</span><span>"http://www.example.org/submit.php"</span><span>,</span> <span>{</span>
|
|
||||||
method<span>:</span> <span>"POST"</span><span>,</span>
|
|
||||||
headers<span>:</span> <span>{</span>
|
|
||||||
<span>"Content-Type"</span><span>:</span> <span>"application/x-www-form-urlencoded"</span>
|
|
||||||
<span>}</span><span>,</span>
|
|
||||||
body<span>:</span> <span>"firstName=Nikhil&favColor=blue&password=easytoguess"</span>
|
|
||||||
<span>}</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>res<span>)</span> <span>{</span>
|
|
||||||
<span>if</span> <span>(</span>res.<span>ok</span><span>)</span> <span>{</span>
|
|
||||||
alert<span>(</span><span>"Perfect! Your settings are saved."</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span> <span>else</span> <span>if</span> <span>(</span>res.<span>status</span> <span>==</span> <span>401</span><span>)</span> <span>{</span>
|
|
||||||
alert<span>(</span><span>"Oops! You are not authorized."</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span>
|
|
||||||
<span>}</span><span>,</span> <span>function</span><span>(</span>e<span>)</span> <span>{</span>
|
|
||||||
alert<span>(</span><span>"Error submitting form!"</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>The <code>fetch()</code> function’s arguments are the same as those passed
|
|
||||||
to the
|
to the
|
||||||
<br>
|
<br><code>Request()</code> constructor, so you may directly pass arbitrarily
|
||||||
<code>Request()</code> constructor, so you may directly pass arbitrarily
|
|
||||||
complex requests to <code>fetch()</code> as discussed below.</p>
|
complex requests to <code>fetch()</code> as discussed below.</p>
|
||||||
|
|
||||||
<h2>Headers</h2>
|
<h2>Headers</h2>
|
||||||
|
<p>Fetch introduces 3 interfaces. These are <code>Headers</code>, <code>Request</code> and
|
||||||
<p>Fetch introduces 3 interfaces. These are <code>Headers</code>, <code>Request</code> and
|
<br><code>Response</code>. They map directly to the underlying HTTP concepts,
|
||||||
<br>
|
|
||||||
<code>Response</code>. They map directly to the underlying HTTP concepts,
|
|
||||||
but have
|
but have
|
||||||
<br>certain visibility filters in place for privacy and security reasons,
|
<br>certain visibility filters in place for privacy and security reasons,
|
||||||
such as
|
such as
|
||||||
<br>supporting CORS rules and ensuring cookies aren’t readable by third parties.</p>
|
<br>supporting CORS rules and ensuring cookies aren’t readable by third parties.</p>
|
||||||
<p>The <a href="https://fetch.spec.whatwg.org/#headers-class" target="_blank">Headers interface</a> is
|
<p>The <a href="https://fetch.spec.whatwg.org/#headers-class" target="_blank">Headers interface</a> is
|
||||||
a simple multi-map of names to values:</p>
|
a simple multi-map of names to values:</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> content <span>=</span><span>"Hello World"</span><span>;</span><span>var</span> reqHeaders <span>=</span><span>new</span> Headers<span>(</span><span>)</span><span>;</span>
|
||||||
<table>
|
reqHeaders.<span>append</span><span>(</span><span>"Content-Type"</span><span>,</span><span>"text/plain"</span>
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre><span>var</span> content <span>=</span> <span>"Hello World"</span><span>;</span>
|
|
||||||
<span>var</span> reqHeaders <span>=</span> <span>new</span> Headers<span>(</span><span>)</span><span>;</span>
|
|
||||||
reqHeaders.<span>append</span><span>(</span><span>"Content-Type"</span><span>,</span> <span>"text/plain"</span>
|
|
||||||
reqHeaders.<span>append</span><span>(</span><span>"Content-Length"</span><span>,</span> content.<span>length</span>.<span>toString</span><span>(</span><span>)</span><span>)</span><span>;</span>
|
reqHeaders.<span>append</span><span>(</span><span>"Content-Length"</span><span>,</span> content.<span>length</span>.<span>toString</span><span>(</span><span>)</span><span>)</span><span>;</span>
|
||||||
reqHeaders.<span>append</span><span>(</span><span>"X-Custom-Header"</span><span>,</span> <span>"ProcessThisImmediately"</span><span>)</span><span>;</span></pre>
|
reqHeaders.<span>append</span><span>(</span><span>"X-Custom-Header"</span><span>,</span><span>"ProcessThisImmediately"</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
</td>
|
<p>The same can be achieved by passing an array of arrays or a JS object
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>The same can be achieved by passing an array of arrays or a JS object
|
|
||||||
literal
|
literal
|
||||||
<br>to the constructor:</p>
|
<br>to the constructor:</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre>reqHeaders <span>=</span><span>new</span> Headers<span>(</span><span>{</span><span>"Content-Type"</span><span>:</span><span>"text/plain"</span><span>,</span><span>"Content-Length"</span><span>:</span> content.<span>length</span>.<span>toString</span><span>(</span><span>)</span><span>,</span><span>"X-Custom-Header"</span><span>:</span><span>"ProcessThisImmediately"</span><span>,</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<table>
|
<p>The contents can be queried and retrieved:</p>
|
||||||
<tbody>
|
<div><table><tbody><tr><td><pre>console.<span>log</span><span>(</span>reqHeaders.<span>has</span><span>(</span><span>"Content-Type"</span><span>)</span><span>)</span><span>;</span><span>// true</span>
|
||||||
<tr>
|
console.<span>log</span><span>(</span>reqHeaders.<span>has</span><span>(</span><span>"Set-Cookie"</span><span>)</span><span>)</span><span>;</span><span>// false</span>
|
||||||
<td>
|
reqHeaders.<span>set</span><span>(</span><span>"Content-Type"</span><span>,</span><span>"text/html"</span><span>)</span><span>;</span>
|
||||||
<pre>reqHeaders <span>=</span> <span>new</span> Headers<span>(</span><span>{</span>
|
reqHeaders.<span>append</span><span>(</span><span>"X-Custom-Header"</span><span>,</span><span>"AnotherValue"</span><span>)</span><span>;</span>
|
||||||
<span>"Content-Type"</span><span>:</span> <span>"text/plain"</span><span>,</span>
|
|
||||||
<span>"Content-Length"</span><span>:</span> content.<span>length</span>.<span>toString</span><span>(</span><span>)</span><span>,</span>
|
|
||||||
<span>"X-Custom-Header"</span><span>:</span> <span>"ProcessThisImmediately"</span><span>,</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>The contents can be queried and retrieved:</p>
|
|
||||||
<P>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre>console.<span>log</span><span>(</span>reqHeaders.<span>has</span><span>(</span><span>"Content-Type"</span><span>)</span><span>)</span><span>;</span> <span>// true</span>
|
|
||||||
console.<span>log</span><span>(</span>reqHeaders.<span>has</span><span>(</span><span>"Set-Cookie"</span><span>)</span><span>)</span><span>;</span> <span>// false</span>
|
|
||||||
reqHeaders.<span>set</span><span>(</span><span>"Content-Type"</span><span>,</span> <span>"text/html"</span><span>)</span><span>;</span>
|
|
||||||
reqHeaders.<span>append</span><span>(</span><span>"X-Custom-Header"</span><span>,</span> <span>"AnotherValue"</span><span>)</span><span>;</span>
|
|
||||||
|
|
||||||
console.<span>log</span><span>(</span>reqHeaders.<span>get</span><span>(</span><span>"Content-Length"</span><span>)</span><span>)</span><span>;</span> <span>// 11</span>
|
console.<span>log</span><span>(</span>reqHeaders.<span>get</span><span>(</span><span>"Content-Length"</span><span>)</span><span>)</span><span>;</span><span>// 11</span>
|
||||||
console.<span>log</span><span>(</span>reqHeaders.<span>getAll</span><span>(</span><span>"X-Custom-Header"</span><span>)</span><span>)</span><span>;</span> <span>// ["ProcessThisImmediately", "AnotherValue"]</span>
|
console.<span>log</span><span>(</span>reqHeaders.<span>getAll</span><span>(</span><span>"X-Custom-Header"</span><span>)</span><span>)</span><span>;</span><span>// ["ProcessThisImmediately", "AnotherValue"]</span>
|
||||||
|
|
||||||
reqHeaders.<span>delete</span><span>(</span><span>"X-Custom-Header"</span><span>)</span><span>;</span>
|
reqHeaders.<span>delete</span><span>(</span><span>"X-Custom-Header"</span><span>)</span><span>;</span>
|
||||||
console.<span>log</span><span>(</span>reqHeaders.<span>getAll</span><span>(</span><span>"X-Custom-Header"</span><span>)</span><span>)</span><span>;</span> <span>// []</span></pre>
|
console.<span>log</span><span>(</span>reqHeaders.<span>getAll</span><span>(</span><span>"X-Custom-Header"</span><span>)</span><span>)</span><span>;</span><span>// []</span></pre></td></tr></tbody></table></div>
|
||||||
</td>
|
<p>Some of these operations are only useful in ServiceWorkers, but they provide
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>Some of these operations are only useful in ServiceWorkers, but they provide
|
|
||||||
<br>a much nicer API to Headers.</p>
|
<br>a much nicer API to Headers.</p>
|
||||||
<p>Since Headers can be sent in requests, or received in responses, and have
|
<p>Since Headers can be sent in requests, or received in responses, and have
|
||||||
various limitations about what information can and should be mutable, <code>Headers</code> objects
|
various limitations about what information can and should be mutable, <code>Headers</code> objects
|
||||||
have a <strong>guard</strong> property. This is not exposed to the Web, but
|
have a <strong>guard</strong> property. This is not exposed to the Web, but
|
||||||
it affects which mutation operations are allowed on the Headers object.
|
it affects which mutation operations are allowed on the Headers object.
|
||||||
<br>Possible values are:</p>
|
<br>Possible values are:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>“none”: default.</li>
|
<li>“none”: default.</li>
|
||||||
<li>“request”: guard for a Headers object obtained from a Request (<code>Request.headers</code>).</li>
|
<li>“request”: guard for a Headers object obtained from a Request (<code>Request.headers</code>).</li>
|
||||||
<li>“request-no-cors”: guard for a Headers object obtained from a Request
|
<li>“request-no-cors”: guard for a Headers object obtained from a Request
|
||||||
created
|
created
|
||||||
<br>with mode “no-cors”.</li>
|
<br>with mode “no-cors”.</li>
|
||||||
<li>“response”: naturally, for Headers obtained from Response (<code>Response.headers</code>).</li>
|
<li>“response”: naturally, for Headers obtained from Response (<code>Response.headers</code>).</li>
|
||||||
<li>“immutable”: Mostly used for ServiceWorkers, renders a Headers object
|
<li>“immutable”: Mostly used for ServiceWorkers, renders a Headers object
|
||||||
<br>read-only.</li>
|
<br>read-only.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The details of how each guard affects the behaviors of the Headers object
|
<p>The details of how each guard affects the behaviors of the Headers object
|
||||||
are
|
are
|
||||||
<br>in the <a href="https://fetch.spec.whatwg.org" target="_blank">specification</a>. For example,
|
<br>in the <a href="https://fetch.spec.whatwg.org" target="_blank">specification</a>. For example,
|
||||||
you may not append or set a “request” guarded Headers’ “Content-Length”
|
you may not append or set a “request” guarded Headers’ “Content-Length”
|
||||||
header. Similarly, inserting “Set-Cookie” into a Response header is not
|
header. Similarly, inserting “Set-Cookie” into a Response header is not
|
||||||
allowed so that ServiceWorkers may not set cookies via synthesized Responses.</p>
|
allowed so that ServiceWorkers may not set cookies via synthesized Responses.</p>
|
||||||
<p>All of the Headers methods throw TypeError if <code>name</code> is not a
|
<p>All of the Headers methods throw TypeError if <code>name</code> is not a
|
||||||
<a href="https://fetch.spec.whatwg.org/#concept-header-name" target="_blank">valid HTTP Header name</a>. The mutation operations will throw TypeError
|
<a href="https://fetch.spec.whatwg.org/#concept-header-name" target="_blank">valid HTTP Header name</a>. The mutation operations will throw TypeError
|
||||||
if there is an immutable guard. Otherwise they fail silently. For example:</p>
|
if there is an immutable guard. Otherwise they fail silently. For example:</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> res <span>=</span> Response.<span>error</span><span>(</span><span>)</span><span>;</span><span>try</span><span>{</span>
|
||||||
<table>
|
res.<span>headers</span>.<span>set</span><span>(</span><span>"Origin"</span><span>,</span><span>"http://mybank.com"</span><span>)</span><span>;</span><span>}</span><span>catch</span><span>(</span>e<span>)</span><span>{</span>
|
||||||
<tbody>
|
console.<span>log</span><span>(</span><span>"Cannot pretend to be a bank!"</span><span>)</span><span>;</span><span>}</span></pre></td></tr></tbody></table></div>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre><span>var</span> res <span>=</span> Response.<span>error</span><span>(</span><span>)</span><span>;</span>
|
|
||||||
<span>try</span> <span>{</span>
|
|
||||||
res.<span>headers</span>.<span>set</span><span>(</span><span>"Origin"</span><span>,</span> <span>"http://mybank.com"</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span> <span>catch</span><span>(</span>e<span>)</span> <span>{</span>
|
|
||||||
console.<span>log</span><span>(</span><span>"Cannot pretend to be a bank!"</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
|
|
||||||
<h2>Request</h2>
|
<h2>Request</h2>
|
||||||
|
<p>The Request interface defines a request to fetch a resource over HTTP.
|
||||||
<p>The Request interface defines a request to fetch a resource over HTTP.
|
|
||||||
URL, method and headers are expected, but the Request also allows specifying
|
URL, method and headers are expected, but the Request also allows specifying
|
||||||
a body, a request mode, credentials and cache hints.</p>
|
a body, a request mode, credentials and cache hints.</p>
|
||||||
<p>The simplest Request is of course, just a URL, as you may do to GET a
|
<p>The simplest Request is of course, just a URL, as you may do to GET a
|
||||||
resource.</p>
|
resource.</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> req <span>=</span><span>new</span> Request<span>(</span><span>"/index.html"</span><span>)</span><span>;</span>
|
||||||
<table>
|
console.<span>log</span><span>(</span>req.<span>method</span><span>)</span><span>;</span><span>// "GET"</span>
|
||||||
<tbody>
|
console.<span>log</span><span>(</span>req.<span>url</span><span>)</span><span>;</span><span>// "http://example.com/index.html"</span></pre></td></tr></tbody></table></div>
|
||||||
<tr>
|
<p>You may also pass a Request to the <code>Request()</code> constructor to
|
||||||
<td>
|
|
||||||
<pre><span>var</span> req <span>=</span> <span>new</span> Request<span>(</span><span>"/index.html"</span><span>)</span><span>;</span>
|
|
||||||
console.<span>log</span><span>(</span>req.<span>method</span><span>)</span><span>;</span> <span>// "GET"</span>
|
|
||||||
console.<span>log</span><span>(</span>req.<span>url</span><span>)</span><span>;</span> <span>// "http://example.com/index.html"</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>You may also pass a Request to the <code>Request()</code> constructor to
|
|
||||||
create a copy.
|
create a copy.
|
||||||
<br>(This is not the same as calling the <code>clone()</code> method, which
|
<br>(This is not the same as calling the <code>clone()</code> method, which
|
||||||
is covered in
|
is covered in
|
||||||
<br>the “Reading bodies” section.).</p>
|
<br>the “Reading bodies” section.).</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> copy <span>=</span><span>new</span> Request<span>(</span>req<span>)</span><span>;</span>
|
||||||
<table>
|
console.<span>log</span><span>(</span>copy.<span>method</span><span>)</span><span>;</span><span>// "GET"</span>
|
||||||
<tbody>
|
console.<span>log</span><span>(</span>copy.<span>url</span><span>)</span><span>;</span><span>// "http://example.com/index.html"</span></pre></td></tr></tbody></table></div>
|
||||||
<tr>
|
<p>Again, this form is probably only useful in ServiceWorkers.</p>
|
||||||
<td>
|
<p>The non-URL attributes of the <code>Request</code> can only be set by passing
|
||||||
<pre><span>var</span> copy <span>=</span> <span>new</span> Request<span>(</span>req<span>)</span><span>;</span>
|
|
||||||
console.<span>log</span><span>(</span>copy.<span>method</span><span>)</span><span>;</span> <span>// "GET"</span>
|
|
||||||
console.<span>log</span><span>(</span>copy.<span>url</span><span>)</span><span>;</span> <span>// "http://example.com/index.html"</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>Again, this form is probably only useful in ServiceWorkers.</p>
|
|
||||||
<p>The non-URL attributes of the <code>Request</code> can only be set by passing
|
|
||||||
initial
|
initial
|
||||||
<br>values as a second argument to the constructor. This argument is a dictionary.</p>
|
<br>values as a second argument to the constructor. This argument is a dictionary.</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> uploadReq <span>=</span><span>new</span> Request<span>(</span><span>"/uploadImage"</span><span>,</span><span>{</span>
|
||||||
<table>
|
method<span>:</span><span>"POST"</span><span>,</span>
|
||||||
<tbody>
|
headers<span>:</span><span>{</span><span>"Content-Type"</span><span>:</span><span>"image/png"</span><span>,</span><span>}</span><span>,</span>
|
||||||
<tr>
|
body<span>:</span><span>"image data"</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<td>
|
<p>The Request’s mode is used to determine if cross-origin requests lead
|
||||||
<pre><span>var</span> uploadReq <span>=</span> <span>new</span> Request<span>(</span><span>"/uploadImage"</span><span>,</span> <span>{</span>
|
|
||||||
method<span>:</span> <span>"POST"</span><span>,</span>
|
|
||||||
headers<span>:</span> <span>{</span>
|
|
||||||
<span>"Content-Type"</span><span>:</span> <span>"image/png"</span><span>,</span>
|
|
||||||
<span>}</span><span>,</span>
|
|
||||||
body<span>:</span> <span>"image data"</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>The Request’s mode is used to determine if cross-origin requests lead
|
|
||||||
to valid responses, and which properties on the response are readable.
|
to valid responses, and which properties on the response are readable.
|
||||||
Legal mode values are <code>"same-origin"</code>, <code>"no-cors"</code> (default)
|
Legal mode values are <code>"same-origin"</code>, <code>"no-cors"</code> (default)
|
||||||
and <code>"cors"</code>.</p>
|
and <code>"cors"</code>.</p>
|
||||||
<p>The <code>"same-origin"</code> mode is simple, if a request is made to another
|
<p>The <code>"same-origin"</code> mode is simple, if a request is made to another
|
||||||
origin with this mode set, the result is simply an error. You could use
|
origin with this mode set, the result is simply an error. You could use
|
||||||
this to ensure that
|
this to ensure that
|
||||||
<br>a request is always being made to your origin.</p>
|
<br>a request is always being made to your origin.</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> arbitraryUrl <span>=</span> document.<span>getElementById</span><span>(</span><span>"url-input"</span><span>)</span>.<span>value</span><span>;</span>
|
||||||
<table>
|
fetch<span>(</span>arbitraryUrl<span>,</span><span>{</span> mode<span>:</span><span>"same-origin"</span><span>}</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>res<span>)</span><span>{</span>
|
||||||
<tbody>
|
console.<span>log</span><span>(</span><span>"Response succeeded?"</span><span>,</span> res.<span>ok</span><span>)</span><span>;</span><span>}</span><span>,</span><span>function</span><span>(</span>e<span>)</span><span>{</span>
|
||||||
<tr>
|
console.<span>log</span><span>(</span><span>"Please enter a same-origin URL!"</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<td>
|
<p>The <code>"no-cors"</code> mode captures what the web platform does by default
|
||||||
<pre><span>var</span> arbitraryUrl <span>=</span> document.<span>getElementById</span><span>(</span><span>"url-input"</span><span>)</span>.<span>value</span><span>;</span>
|
|
||||||
fetch<span>(</span>arbitraryUrl<span>,</span> <span>{</span> mode<span>:</span> <span>"same-origin"</span> <span>}</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>res<span>)</span> <span>{</span>
|
|
||||||
console.<span>log</span><span>(</span><span>"Response succeeded?"</span><span>,</span> res.<span>ok</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>,</span> <span>function</span><span>(</span>e<span>)</span> <span>{</span>
|
|
||||||
console.<span>log</span><span>(</span><span>"Please enter a same-origin URL!"</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>The <code>"no-cors"</code> mode captures what the web platform does by default
|
|
||||||
for scripts you import from CDNs, images hosted on other domains, and so
|
for scripts you import from CDNs, images hosted on other domains, and so
|
||||||
on. First, it prevents the method from being anything other than “HEAD”,
|
on. First, it prevents the method from being anything other than “HEAD”,
|
||||||
“GET” or “POST”. Second, if any ServiceWorkers intercept these requests,
|
“GET” or “POST”. Second, if any ServiceWorkers intercept these requests,
|
||||||
|
@ -287,7 +147,7 @@ fetch<span>(</span>arbitraryUrl<span>,</span> <span>{</span> mode<span>:</span>
|
||||||
This ensures that ServiceWorkers do not affect the semantics of the Web
|
This ensures that ServiceWorkers do not affect the semantics of the Web
|
||||||
and prevents security and privacy issues that could arise from leaking
|
and prevents security and privacy issues that could arise from leaking
|
||||||
data across domains.</p>
|
data across domains.</p>
|
||||||
<p><code>"cors"</code> mode is what you’ll usually use to make known cross-origin
|
<p><code>"cors"</code> mode is what you’ll usually use to make known cross-origin
|
||||||
requests to access various APIs offered by other vendors. These are expected
|
requests to access various APIs offered by other vendors. These are expected
|
||||||
to adhere to
|
to adhere to
|
||||||
<br>the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS" target="_blank">CORS protocol</a>.
|
<br>the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS" target="_blank">CORS protocol</a>.
|
||||||
|
@ -295,300 +155,175 @@ fetch<span>(</span>arbitraryUrl<span>,</span> <span>{</span> mode<span>:</span>
|
||||||
headers is exposed in the Response, but the body is readable. For example,
|
headers is exposed in the Response, but the body is readable. For example,
|
||||||
you could get a list of Flickr’s <a href="https://www.flickr.com/services/api/flickr.interestingness.getList.html" target="_blank">most interesting</a> photos
|
you could get a list of Flickr’s <a href="https://www.flickr.com/services/api/flickr.interestingness.getList.html" target="_blank">most interesting</a> photos
|
||||||
today like this:</p>
|
today like this:</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> u <span>=</span><span>new</span> URLSearchParams<span>(</span><span>)</span><span>;</span>
|
||||||
<table>
|
u.<span>append</span><span>(</span><span>'method'</span><span>,</span><span>'flickr.interestingness.getList'</span><span>)</span><span>;</span>
|
||||||
<tbody>
|
u.<span>append</span><span>(</span><span>'api_key'</span><span>,</span><span>'<insert api key here>'</span><span>)</span><span>;</span>
|
||||||
<tr>
|
u.<span>append</span><span>(</span><span>'format'</span><span>,</span><span>'json'</span><span>)</span><span>;</span>
|
||||||
<td>
|
u.<span>append</span><span>(</span><span>'nojsoncallback'</span><span>,</span><span>'1'</span><span>)</span><span>;</span><span>var</span> apiCall <span>=</span> fetch<span>(</span><span>'https://api.flickr.com/services/rest?'</span><span>+</span> u<span>)</span><span>;</span>
|
||||||
<pre><span>var</span> u <span>=</span> <span>new</span> URLSearchParams<span>(</span><span>)</span><span>;</span>
|
|
||||||
u.<span>append</span><span>(</span><span>'method'</span><span>,</span> <span>'flickr.interestingness.getList'</span><span>)</span><span>;</span>
|
|
||||||
u.<span>append</span><span>(</span><span>'api_key'</span><span>,</span> <span>'<insert api key here>'</span><span>)</span><span>;</span>
|
|
||||||
u.<span>append</span><span>(</span><span>'format'</span><span>,</span> <span>'json'</span><span>)</span><span>;</span>
|
|
||||||
u.<span>append</span><span>(</span><span>'nojsoncallback'</span><span>,</span> <span>'1'</span><span>)</span><span>;</span>
|
|
||||||
|
|
||||||
<span>var</span> apiCall <span>=</span> fetch<span>(</span><span>'https://api.flickr.com/services/rest?'</span> <span>+</span> u<span>)</span><span>;</span>
|
apiCall.<span>then</span><span>(</span><span>function</span><span>(</span>response<span>)</span><span>{</span><span>return</span> response.<span>json</span><span>(</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>json<span>)</span><span>{</span><span>// photo is a list of photos.</span><span>return</span> json.<span>photos</span>.<span>photo</span><span>;</span><span>}</span><span>)</span><span>;</span><span>}</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>photos<span>)</span><span>{</span>
|
||||||
|
photos.<span>forEach</span><span>(</span><span>function</span><span>(</span>photo<span>)</span><span>{</span>
|
||||||
apiCall.<span>then</span><span>(</span><span>function</span><span>(</span>response<span>)</span> <span>{</span>
|
console.<span>log</span><span>(</span>photo.<span>title</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<span>return</span> response.<span>json</span><span>(</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>json<span>)</span> <span>{</span>
|
<p>You may not read out the “Date” header since Flickr does not allow it
|
||||||
<span>// photo is a list of photos.</span>
|
|
||||||
<span>return</span> json.<span>photos</span>.<span>photo</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>photos<span>)</span> <span>{</span>
|
|
||||||
photos.<span>forEach</span><span>(</span><span>function</span><span>(</span>photo<span>)</span> <span>{</span>
|
|
||||||
console.<span>log</span><span>(</span>photo.<span>title</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>You may not read out the “Date” header since Flickr does not allow it
|
|
||||||
via
|
via
|
||||||
<br>
|
<br><code>Access-Control-Expose-Headers</code>.</p>
|
||||||
<code>Access-Control-Expose-Headers</code>.</p>
|
<div><table><tbody><tr><td><pre>response.<span>headers</span>.<span>get</span><span>(</span><span>"Date"</span><span>)</span><span>;</span><span>// null</span></pre></td></tr></tbody></table></div>
|
||||||
<P>
|
<p>The <code>credentials</code> enumeration determines if cookies for the other
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre>response.<span>headers</span>.<span>get</span><span>(</span><span>"Date"</span><span>)</span><span>;</span> <span>// null</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>The <code>credentials</code> enumeration determines if cookies for the other
|
|
||||||
domain are
|
domain are
|
||||||
<br>sent to cross-origin requests. This is similar to XHR’s <code>withCredentials</code>
|
<br>sent to cross-origin requests. This is similar to XHR’s <code>withCredentials</code><br>flag, but tri-valued as <code>"omit"</code> (default), <code>"same-origin"</code> and <code>"include"</code>.</p>
|
||||||
<br>flag, but tri-valued as <code>"omit"</code> (default), <code>"same-origin"</code> and <code>"include"</code>.</p>
|
<p>The Request object will also give the ability to offer caching hints to
|
||||||
<p>The Request object will also give the ability to offer caching hints to
|
|
||||||
the user-agent. This is currently undergoing some <a href="https://github.com/slightlyoff/ServiceWorker/issues/585" target="_blank">security review</a>.
|
the user-agent. This is currently undergoing some <a href="https://github.com/slightlyoff/ServiceWorker/issues/585" target="_blank">security review</a>.
|
||||||
Firefox exposes the attribute, but it has no effect.</p>
|
Firefox exposes the attribute, but it has no effect.</p>
|
||||||
<p>Requests have two read-only attributes that are relevant to ServiceWorkers
|
<p>Requests have two read-only attributes that are relevant to ServiceWorkers
|
||||||
<br>intercepting them. There is the string <code>referrer</code>, which is
|
<br>intercepting them. There is the string <code>referrer</code>, which is
|
||||||
set by the UA to be
|
set by the UA to be
|
||||||
<br>the referrer of the Request. This may be an empty string. The other is
|
<br>the referrer of the Request. This may be an empty string. The other is
|
||||||
<br>
|
<br><code>context</code> which is a rather <a href="https://fetch.spec.whatwg.org/#requestcredentials" target="_blank">large enumeration</a> defining
|
||||||
<code>context</code> which is a rather <a href="https://fetch.spec.whatwg.org/#requestcredentials" target="_blank">large enumeration</a> defining
|
|
||||||
what sort of resource is being fetched. This could be “image” if the request
|
what sort of resource is being fetched. This could be “image” if the request
|
||||||
is from an
|
is from an
|
||||||
<img>tag in the controlled document, “worker” if it is an attempt to load a
|
<img>tag in the controlled document, “worker” if it is an attempt to load a
|
||||||
worker script, and so on. When used with the <code>fetch()</code> function,
|
worker script, and so on. When used with the <code>fetch()</code> function,
|
||||||
it is “fetch”.</p>
|
it is “fetch”.</p>
|
||||||
|
|
||||||
<h2>Response</h2>
|
<h2>Response</h2>
|
||||||
|
<p><code>Response</code> instances are returned by calls to <code>fetch()</code>.
|
||||||
<p><code>Response</code> instances are returned by calls to <code>fetch()</code>.
|
|
||||||
They can also be created by JS, but this is only useful in ServiceWorkers.</p>
|
They can also be created by JS, but this is only useful in ServiceWorkers.</p>
|
||||||
<p>We have already seen some attributes of Response when we looked at <code>fetch()</code>.
|
<p>We have already seen some attributes of Response when we looked at <code>fetch()</code>.
|
||||||
The most obvious candidates are <code>status</code>, an integer (default
|
The most obvious candidates are <code>status</code>, an integer (default
|
||||||
value 200) and <code>statusText</code> (default value “OK”), which correspond
|
value 200) and <code>statusText</code> (default value “OK”), which correspond
|
||||||
to the HTTP status code and reason. The <code>ok</code> attribute is just
|
to the HTTP status code and reason. The <code>ok</code> attribute is just
|
||||||
a shorthand for checking that <code>status</code> is in the range 200-299
|
a shorthand for checking that <code>status</code> is in the range 200-299
|
||||||
inclusive.</p>
|
inclusive.</p>
|
||||||
<p><code>headers</code> is the Response’s Headers object, with guard “response”.
|
<p><code>headers</code> is the Response’s Headers object, with guard “response”.
|
||||||
The <code>url</code> attribute reflects the URL of the corresponding request.</p>
|
The <code>url</code> attribute reflects the URL of the corresponding request.</p>
|
||||||
<p>Response also has a <code>type</code>, which is “basic”, “cors”, “default”,
|
<p>Response also has a <code>type</code>, which is “basic”, “cors”, “default”,
|
||||||
“error” or
|
“error” or
|
||||||
<br>“opaque”.</p>
|
<br>“opaque”.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<code>"basic"</code>: normal, same origin response, with all headers exposed
|
<code>"basic"</code>: normal, same origin response, with all headers exposed
|
||||||
except
|
except
|
||||||
<br>“Set-Cookie” and “Set-Cookie2″.</li>
|
<br>“Set-Cookie” and “Set-Cookie2″.</li>
|
||||||
<li>
|
<li>
|
||||||
<code>"cors"</code>: response was received from a valid cross-origin request.
|
<code>"cors"</code>: response was received from a valid cross-origin request.
|
||||||
<a href="https://fetch.spec.whatwg.org/#concept-filtered-response-cors" target="_blank">Certain headers and the body</a>may be accessed.</li>
|
<a href="https://fetch.spec.whatwg.org/#concept-filtered-response-cors" target="_blank">Certain headers and the body</a>may be accessed.</li>
|
||||||
<li>
|
<li>
|
||||||
<code>"error"</code>: network error. No useful information describing
|
<code>"error"</code>: network error. No useful information describing
|
||||||
the error is available. The Response’s status is 0, headers are empty and
|
the error is available. The Response’s status is 0, headers are empty and
|
||||||
immutable. This is the type for a Response obtained from <code>Response.error()</code>.</li>
|
immutable. This is the type for a Response obtained from <code>Response.error()</code>.</li>
|
||||||
<li>
|
<li>
|
||||||
<code>"opaque"</code>: response for “no-cors” request to cross-origin
|
<code>"opaque"</code>: response for “no-cors” request to cross-origin
|
||||||
resource. <a href="https://fetch.spec.whatwg.org/#concept-filtered-response-opaque" target="_blank">Severely<br>
|
resource. <a href="https://fetch.spec.whatwg.org/#concept-filtered-response-opaque" target="_blank">Severely<br>
|
||||||
restricted</a>
|
restricted</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The “error” type results in the <code>fetch()</code> Promise rejecting with
|
<p>The “error” type results in the <code>fetch()</code> Promise rejecting with
|
||||||
TypeError.</p>
|
TypeError.</p>
|
||||||
<p>There are certain attributes that are useful only in a ServiceWorker scope.
|
<p>There are certain attributes that are useful only in a ServiceWorker scope.
|
||||||
The
|
The
|
||||||
<br>idiomatic way to return a Response to an intercepted request in ServiceWorkers
|
<br>idiomatic way to return a Response to an intercepted request in ServiceWorkers
|
||||||
is:</p>
|
is:</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre>addEventListener<span>(</span><span>'fetch'</span><span>,</span><span>function</span><span>(</span>event<span>)</span><span>{</span>
|
||||||
<table>
|
event.<span>respondWith</span><span>(</span><span>new</span> Response<span>(</span><span>"Response body"</span><span>,</span><span>{</span>
|
||||||
<tbody>
|
headers<span>:</span><span>{</span><span>"Content-Type"</span><span>:</span><span>"text/plain"</span><span>}</span><span>}</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<tr>
|
<p>As you can see, Response has a two argument constructor, where both arguments
|
||||||
<td>
|
|
||||||
<pre>addEventListener<span>(</span><span>'fetch'</span><span>,</span> <span>function</span><span>(</span>event<span>)</span> <span>{</span>
|
|
||||||
event.<span>respondWith</span><span>(</span><span>new</span> Response<span>(</span><span>"Response body"</span><span>,</span> <span>{</span>
|
|
||||||
headers<span>:</span> <span>{</span> <span>"Content-Type"</span> <span>:</span> <span>"text/plain"</span> <span>}</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>As you can see, Response has a two argument constructor, where both arguments
|
|
||||||
are optional. The first argument is a body initializer, and the second
|
are optional. The first argument is a body initializer, and the second
|
||||||
is a dictionary to set the <code>status</code>, <code>statusText</code> and <code>headers</code>.</p>
|
is a dictionary to set the <code>status</code>, <code>statusText</code> and <code>headers</code>.</p>
|
||||||
<p>The static method <code>Response.error()</code> simply returns an error
|
<p>The static method <code>Response.error()</code> simply returns an error
|
||||||
response. Similarly, <code>Response.redirect(url, status)</code> returns
|
response. Similarly, <code>Response.redirect(url, status)</code> returns
|
||||||
a Response resulting in
|
a Response resulting in
|
||||||
<br>a redirect to <code>url</code>.</p>
|
<br>a redirect to <code>url</code>.</p>
|
||||||
|
|
||||||
<h2>Dealing with bodies</h2>
|
<h2>Dealing with bodies</h2>
|
||||||
|
<p>Both Requests and Responses may contain body data. We’ve been glossing
|
||||||
<p>Both Requests and Responses may contain body data. We’ve been glossing
|
|
||||||
over it because of the various data types body may contain, but we will
|
over it because of the various data types body may contain, but we will
|
||||||
cover it in detail now.</p>
|
cover it in detail now.</p>
|
||||||
<p>A body is an instance of any of the following types.</p>
|
<p>A body is an instance of any of the following types.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer" target="_blank">ArrayBuffer</a></li>
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer" target="_blank">ArrayBuffer</a>
|
<li>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView" target="_blank">ArrayBufferView</a> (Uint8Array
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView" target="_blank">ArrayBufferView</a> (Uint8Array
|
||||||
and friends)</li>
|
and friends)</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob" target="_blank">Blob</a>/
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob" target="_blank">Blob</a>/
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/File" target="_blank">File</a>
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/File" target="_blank">File</a>
|
||||||
</li>
|
</li>
|
||||||
<li>string</li>
|
<li>string</li>
|
||||||
<li>
|
<li><a href="https://url.spec.whatwg.org/#interface-urlsearchparams" target="_blank">URLSearchParams</a></li>
|
||||||
<a href="https://url.spec.whatwg.org/#interface-urlsearchparams" target="_blank">URLSearchParams</a>
|
<li>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData" target="_blank">FormData</a> –
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData" target="_blank">FormData</a> –
|
||||||
currently not supported by either Gecko or Blink. Firefox expects to ship
|
currently not supported by either Gecko or Blink. Firefox expects to ship
|
||||||
this in version 39 along with the rest of Fetch.</li>
|
this in version 39 along with the rest of Fetch.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>In addition, Request and Response both offer the following methods to
|
<p>In addition, Request and Response both offer the following methods to
|
||||||
extract their body. These all return a Promise that is eventually resolved
|
extract their body. These all return a Promise that is eventually resolved
|
||||||
with the actual content.</p>
|
with the actual content.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li><code>arrayBuffer()</code></li>
|
||||||
<code>arrayBuffer()</code>
|
<li><code>blob()</code></li>
|
||||||
</li>
|
<li><code>json()</code></li>
|
||||||
<li>
|
<li><code>text()</code></li>
|
||||||
<code>blob()</code>
|
<li><code>formData()</code></li>
|
||||||
</li>
|
</ul>
|
||||||
<li>
|
<p>This is a significant improvement over XHR in terms of ease of use of
|
||||||
<code>json()</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>text()</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>formData()</code>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>This is a significant improvement over XHR in terms of ease of use of
|
|
||||||
non-text data!</p>
|
non-text data!</p>
|
||||||
<p>Request bodies can be set by passing <code>body</code> parameters:</p>
|
<p>Request bodies can be set by passing <code>body</code> parameters:</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> form <span>=</span><span>new</span> FormData<span>(</span>document.<span>getElementById</span><span>(</span><span>'login-form'</span><span>)</span><span>)</span><span>;</span>
|
||||||
<table>
|
fetch<span>(</span><span>"/login"</span><span>,</span><span>{</span>
|
||||||
<tbody>
|
method<span>:</span><span>"POST"</span><span>,</span>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre><span>var</span> form <span>=</span> <span>new</span> FormData<span>(</span>document.<span>getElementById</span><span>(</span><span>'login-form'</span><span>)</span><span>)</span><span>;</span>
|
|
||||||
fetch<span>(</span><span>"/login"</span><span>,</span> <span>{</span>
|
|
||||||
method<span>:</span> <span>"POST"</span><span>,</span>
|
|
||||||
body<span>:</span> form
|
body<span>:</span> form
|
||||||
<span>}</span><span>)</span></pre>
|
<span>}</span><span>)</span></pre></td></tr></tbody></table></div>
|
||||||
</td>
|
<p>Responses take the first argument as the body.</p>
|
||||||
</tr>
|
<div><table><tbody><tr><td><pre><span>var</span> res <span>=</span><span>new</span> Response<span>(</span><span>new</span> File<span>(</span><span>[</span><span>"chunk"</span><span>,</span><span>"chunk"</span><span>]</span><span>,</span><span>"archive.zip"</span><span>,</span><span>{</span> type<span>:</span><span>"application/zip"</span><span>}</span><span>)</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
</tbody>
|
<p>Both Request and Response (and by extension the <code>fetch()</code> function),
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>Responses take the first argument as the body.</p>
|
|
||||||
<P>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre><span>var</span> res <span>=</span> <span>new</span> Response<span>(</span><span>new</span> File<span>(</span><span>[</span><span>"chunk"</span><span>,</span> <span>"chunk"</span><span>]</span><span>,</span> <span>"archive.zip"</span><span>,</span>
|
|
||||||
<span>{</span> type<span>:</span> <span>"application/zip"</span> <span>}</span><span>)</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>Both Request and Response (and by extension the <code>fetch()</code> function),
|
|
||||||
will try to intelligently <a href="https://fetch.spec.whatwg.org/#concept-bodyinit-extract" target="_blank">determine the content type</a>.
|
will try to intelligently <a href="https://fetch.spec.whatwg.org/#concept-bodyinit-extract" target="_blank">determine the content type</a>.
|
||||||
Request will also automatically set a “Content-Type” header if none is
|
Request will also automatically set a “Content-Type” header if none is
|
||||||
set in the dictionary.</p>
|
set in the dictionary.</p>
|
||||||
|
|
||||||
<h3>Streams and cloning</h3>
|
<h3>Streams and cloning</h3>
|
||||||
|
<p>It is important to realise that Request and Response bodies can only be
|
||||||
<p>It is important to realise that Request and Response bodies can only be
|
|
||||||
read once! Both interfaces have a boolean attribute <code>bodyUsed</code> to
|
read once! Both interfaces have a boolean attribute <code>bodyUsed</code> to
|
||||||
determine if it is safe to read or not.</p>
|
determine if it is safe to read or not.</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre><span>var</span> res <span>=</span><span>new</span> Response<span>(</span><span>"one time use"</span><span>)</span><span>;</span>
|
||||||
<table>
|
console.<span>log</span><span>(</span>res.<span>bodyUsed</span><span>)</span><span>;</span><span>// false</span>
|
||||||
<tbody>
|
res.<span>text</span><span>(</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>v<span>)</span><span>{</span>
|
||||||
<tr>
|
console.<span>log</span><span>(</span>res.<span>bodyUsed</span><span>)</span><span>;</span><span>// true</span><span>}</span><span>)</span><span>;</span>
|
||||||
<td>
|
console.<span>log</span><span>(</span>res.<span>bodyUsed</span><span>)</span><span>;</span><span>// true</span>
|
||||||
<pre><span>var</span> res <span>=</span> <span>new</span> Response<span>(</span><span>"one time use"</span><span>)</span><span>;</span>
|
|
||||||
console.<span>log</span><span>(</span>res.<span>bodyUsed</span><span>)</span><span>;</span> <span>// false</span>
|
|
||||||
res.<span>text</span><span>(</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>v<span>)</span> <span>{</span>
|
|
||||||
console.<span>log</span><span>(</span>res.<span>bodyUsed</span><span>)</span><span>;</span> <span>// true</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span>
|
|
||||||
console.<span>log</span><span>(</span>res.<span>bodyUsed</span><span>)</span><span>;</span> <span>// true</span>
|
|
||||||
|
|
||||||
res.<span>text</span><span>(</span><span>)</span>.<span>catch</span><span>(</span><span>function</span><span>(</span>e<span>)</span> <span>{</span>
|
res.<span>text</span><span>(</span><span>)</span>.<span>catch</span><span>(</span><span>function</span><span>(</span>e<span>)</span><span>{</span>
|
||||||
console.<span>log</span><span>(</span><span>"Tried to read already consumed Response"</span><span>)</span><span>;</span>
|
console.<span>log</span><span>(</span><span>"Tried to read already consumed Response"</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
<p>This decision allows easing the transition to an eventual <a href="https://streams.spec.whatwg.org/" target="_blank">stream-based</a> Fetch
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
<p>This decision allows easing the transition to an eventual <a href="https://streams.spec.whatwg.org/" target="_blank">stream-based</a> Fetch
|
|
||||||
API. The intention is to let applications consume data as it arrives, allowing
|
API. The intention is to let applications consume data as it arrives, allowing
|
||||||
for JavaScript to deal with larger files like videos, and perform things
|
for JavaScript to deal with larger files like videos, and perform things
|
||||||
like compression and editing on the fly.</p>
|
like compression and editing on the fly.</p>
|
||||||
<p>Often, you’ll want access to the body multiple times. For example, you
|
<p>Often, you’ll want access to the body multiple times. For example, you
|
||||||
can use the upcoming <a href="http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache-objects" target="_blank">Cache API</a> to
|
can use the upcoming <a href="http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache-objects" target="_blank">Cache API</a> to
|
||||||
store Requests and Responses for offline use, and Cache requires bodies
|
store Requests and Responses for offline use, and Cache requires bodies
|
||||||
to be available for reading.</p>
|
to be available for reading.</p>
|
||||||
<p>So how do you read out the body multiple times within such constraints?
|
<p>So how do you read out the body multiple times within such constraints?
|
||||||
The API provides a <code>clone()</code> method on the two interfaces. This
|
The API provides a <code>clone()</code> method on the two interfaces. This
|
||||||
will return a clone of the object, with a ‘new’ body. <code>clone()</code> MUST
|
will return a clone of the object, with a ‘new’ body. <code>clone()</code> MUST
|
||||||
be called before the body of the corresponding object has been used. That
|
be called before the body of the corresponding object has been used. That
|
||||||
is, <code>clone()</code> first, read later.</p>
|
is, <code>clone()</code> first, read later.</p>
|
||||||
<P>
|
<div><table><tbody><tr><td><pre>addEventListener<span>(</span><span>'fetch'</span><span>,</span><span>function</span><span>(</span>evt<span>)</span><span>{</span><span>var</span> sheep <span>=</span><span>new</span> Response<span>(</span><span>"Dolly"</span><span>)</span><span>;</span>
|
||||||
<table>
|
console.<span>log</span><span>(</span>sheep.<span>bodyUsed</span><span>)</span><span>;</span><span>// false</span><span>var</span> clone <span>=</span> sheep.<span>clone</span><span>(</span><span>)</span><span>;</span>
|
||||||
<tbody>
|
console.<span>log</span><span>(</span>clone.<span>bodyUsed</span><span>)</span><span>;</span><span>// false</span>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<pre>addEventListener<span>(</span><span>'fetch'</span><span>,</span> <span>function</span><span>(</span>evt<span>)</span> <span>{</span>
|
|
||||||
<span>var</span> sheep <span>=</span> <span>new</span> Response<span>(</span><span>"Dolly"</span><span>)</span><span>;</span>
|
|
||||||
console.<span>log</span><span>(</span>sheep.<span>bodyUsed</span><span>)</span><span>;</span> <span>// false</span>
|
|
||||||
<span>var</span> clone <span>=</span> sheep.<span>clone</span><span>(</span><span>)</span><span>;</span>
|
|
||||||
console.<span>log</span><span>(</span>clone.<span>bodyUsed</span><span>)</span><span>;</span> <span>// false</span>
|
|
||||||
|
|
||||||
clone.<span>text</span><span>(</span><span>)</span><span>;</span>
|
clone.<span>text</span><span>(</span><span>)</span><span>;</span>
|
||||||
console.<span>log</span><span>(</span>sheep.<span>bodyUsed</span><span>)</span><span>;</span> <span>// false</span>
|
console.<span>log</span><span>(</span>sheep.<span>bodyUsed</span><span>)</span><span>;</span><span>// false</span>
|
||||||
console.<span>log</span><span>(</span>clone.<span>bodyUsed</span><span>)</span><span>;</span> <span>// true</span>
|
console.<span>log</span><span>(</span>clone.<span>bodyUsed</span><span>)</span><span>;</span><span>// true</span>
|
||||||
|
|
||||||
evt.<span>respondWith</span><span>(</span>cache.<span>add</span><span>(</span>sheep.<span>clone</span><span>(</span><span>)</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>e<span>)</span> <span>{</span>
|
evt.<span>respondWith</span><span>(</span>cache.<span>add</span><span>(</span>sheep.<span>clone</span><span>(</span><span>)</span><span>)</span>.<span>then</span><span>(</span><span>function</span><span>(</span>e<span>)</span><span>{</span><span>return</span> sheep<span>;</span><span>}</span><span>)</span><span>;</span><span>}</span><span>)</span><span>;</span></pre></td></tr></tbody></table></div>
|
||||||
<span>return</span> sheep<span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span>
|
|
||||||
<span>}</span><span>)</span><span>;</span></pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</P>
|
|
||||||
|
|
||||||
<h2>Future improvements</h2>
|
<h2>Future improvements</h2>
|
||||||
|
<p>Along with the transition to streams, Fetch will eventually have the ability
|
||||||
<p>Along with the transition to streams, Fetch will eventually have the ability
|
|
||||||
to abort running <code>fetch()</code>es and some way to report the progress
|
to abort running <code>fetch()</code>es and some way to report the progress
|
||||||
of a fetch. These are provided by XHR, but are a little tricky to fit in
|
of a fetch. These are provided by XHR, but are a little tricky to fit in
|
||||||
the Promise-based nature of the Fetch API.</p>
|
the Promise-based nature of the Fetch API.</p>
|
||||||
<p>You can contribute to the evolution of this API by participating in discussions
|
<p>You can contribute to the evolution of this API by participating in discussions
|
||||||
on the <a href="https://whatwg.org/mailing-list" target="_blank">WHATWG mailing list</a> and
|
on the <a href="https://whatwg.org/mailing-list" target="_blank">WHATWG mailing list</a> and
|
||||||
in the issues in the <a href="https://www.w3.org/Bugs/Public/buglist.cgi?product=WHATWG&component=Fetch&resolution=---" target="_blank">Fetch</a> and
|
in the issues in the <a href="https://www.w3.org/Bugs/Public/buglist.cgi?product=WHATWG&component=Fetch&resolution=---" target="_blank">Fetch</a> and
|
||||||
<a href="https://github.com/slightlyoff/ServiceWorker/issues" target="_blank">ServiceWorker</a>specifications.</p>
|
<a href="https://github.com/slightlyoff/ServiceWorker/issues" target="_blank">ServiceWorker</a>specifications.</p>
|
||||||
<p>For a better web!</p>
|
<p>For a better web!</p>
|
||||||
<p><em>The author would like to thank Andrea Marchesini, Anne van Kesteren and Ben<br>
|
<p><em>The author would like to thank Andrea Marchesini, Anne van Kesteren and Ben<br>
|
||||||
Kelly for helping with the specification and implementation.</em>
|
Kelly for helping with the specification and implementation.</em></p>
|
||||||
</p>
|
<footer><p>Posted by <a href="https://hacks.mozilla.org/author/nmarathemozilla-com/" title="Posts by Nikhil Marathe" rel="author" target="_blank">Nikhil Marathe</a>
|
||||||
<footer>
|
|
||||||
<p>Posted by <a href="https://hacks.mozilla.org/author/nmarathemozilla-com/" title="Posts by Nikhil Marathe" rel="author" target="_blank">Nikhil Marathe</a>
|
|
||||||
on
|
on
|
||||||
<time datetime="2015-03-10T08:05:41-07:00">March 10, 2015</time>at
|
<time datetime="2015-03-10T08:05:41-07:00">March 10, 2015</time>at
|
||||||
<time datetime="PDT08:05:41-07:00">08:05</time>
|
<time datetime="PDT08:05:41-07:00">08:05</time></p></footer></article></DIV></article>
|
||||||
</p>
|
|
||||||
<P>
|
|
||||||
|
|
||||||
</P>
|
|
||||||
</footer>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
|
|
||||||
</DIV></article>
|
|
||||||
|
|
68
resources/tests/readability/webmd-1/expected.html
Normal file
68
resources/tests/readability/webmd-1/expected.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<article><DIV id="readability-page-1" class="page">
|
||||||
|
<div id="readspeaker_area"><P id="readspeaker_button1"></P></div>
|
||||||
|
<div id="textArea">
|
||||||
|
<P></P>
|
||||||
|
<p></p>
|
||||||
|
<p>Feb. 23, 2015 -- Life-threatening peanut allergies have mysteriously
|
||||||
|
been
|
||||||
|
on the rise in the past decade, with little hope for a cure.</p>
|
||||||
|
<p xmlns:xalan="http://xml.apache.org/xalan">But a groundbreaking new
|
||||||
|
study may offer a way to stem that rise, while
|
||||||
|
another may offer some hope for those who are already allergic.</p>
|
||||||
|
<p>Parents have been told for years to avoid giving foods containing
|
||||||
|
peanuts
|
||||||
|
to babies for fear of triggering an allergy. Now research shows the
|
||||||
|
opposite
|
||||||
|
is true: Feeding babies snacks made with peanuts before their first
|
||||||
|
birthday
|
||||||
|
appears to prevent that from happening.</p>
|
||||||
|
<p>The study is published in the <i>New England Journal of Medicine,</i>
|
||||||
|
and
|
||||||
|
it was presented at the annual meeting of the American Academy of
|
||||||
|
Allergy,
|
||||||
|
Asthma and Immunology in Houston. It found that among children at
|
||||||
|
high
|
||||||
|
risk for getting peanut allergies, eating peanut snacks by 11 months
|
||||||
|
of
|
||||||
|
age and continuing to eat them at least three times a week until age
|
||||||
|
5
|
||||||
|
cut their chances of becoming allergic by more than 80% compared to
|
||||||
|
kids
|
||||||
|
who avoided peanuts. Those at high risk were already allergic to
|
||||||
|
egg, they
|
||||||
|
had the skin condition <a href="http://www.webmd.com/skin-problems-and-treatments/eczema/default.htm" target="_blank">eczema</a>, or
|
||||||
|
both.</p>
|
||||||
|
<p>Overall, about 3% of kids who ate peanut butter or peanut snacks
|
||||||
|
before
|
||||||
|
their first birthday got an allergy, compared to about 17% of kids
|
||||||
|
who
|
||||||
|
didn’t eat them.</p>
|
||||||
|
<p>“I think this study is an astounding and groundbreaking study,
|
||||||
|
really,”
|
||||||
|
says Katie Allen, MD, PhD. She's the director of the Center for Food
|
||||||
|
and
|
||||||
|
Allergy Research at the Murdoch Children’s Research Institute in
|
||||||
|
Melbourne,
|
||||||
|
Australia. Allen was not involved in the research.</p>
|
||||||
|
<p>Experts say the research should shift thinking about how kids develop
|
||||||
|
<a href="http://www.webmd.com/allergies/guide/food-allergy-intolerances" target="_blank">food
|
||||||
|
allergies</a>, and it should change the guidance doctors give to
|
||||||
|
parents.
|
||||||
|
</p>
|
||||||
|
<p>Meanwhile, for children and adults who are already <a href="http://www.webmd.com/allergies/guide/nut-allergy" target="_blank">allergic to peanuts</a>,
|
||||||
|
another study presented at the same meeting held out hope of a
|
||||||
|
treatment.</p>
|
||||||
|
<p>A new skin patch called Viaskin allowed people with peanut allergies
|
||||||
|
to
|
||||||
|
eat tiny amounts of peanuts after they wore it for a year.</p>
|
||||||
|
<p></p>
|
||||||
|
<h3>A Change in Guidelines?</h3>
|
||||||
|
<p>Allergies to peanuts and other foods are on the rise. In the U.S.,
|
||||||
|
more
|
||||||
|
than 2% of people react to peanuts, a 400% increase since 1997. And
|
||||||
|
reactions
|
||||||
|
to peanuts and other tree nuts can be especially severe. Nuts are
|
||||||
|
the main
|
||||||
|
reason people get a life-threatening problem called <a href="http://www.webmd.com/allergies/guide/anaphylaxis" target="_blank">anaphylaxis</a>.</p>
|
||||||
|
</div>
|
||||||
|
</DIV></article>
|
2948
resources/tests/readability/webmd-1/source.html
Normal file
2948
resources/tests/readability/webmd-1/source.html
Normal file
File diff suppressed because it is too large
Load diff
|
@ -40,7 +40,7 @@ impl FullTextParser {
|
||||||
url: &url::Url,
|
url: &url::Url,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
) -> Result<Article, FullTextParserError> {
|
) -> Result<Article, FullTextParserError> {
|
||||||
libxml::tree::node::set_node_rc_guard(3);
|
libxml::tree::node::set_node_rc_guard(4);
|
||||||
|
|
||||||
info!("Scraping article: '{}'", url.as_str());
|
info!("Scraping article: '{}'", url.as_str());
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,12 @@ impl Readability {
|
||||||
|
|
||||||
while let Some(node_ref) = node.as_mut() {
|
while let Some(node_ref) = node.as_mut() {
|
||||||
let tag_name = node_ref.get_name().to_uppercase();
|
let tag_name = node_ref.get_name().to_uppercase();
|
||||||
|
|
||||||
|
if tag_name == "TEXT" && node_ref.get_content().trim().is_empty() {
|
||||||
|
node = Util::remove_and_next(node_ref);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let match_string = node_ref
|
let match_string = node_ref
|
||||||
.get_class_names()
|
.get_class_names()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -107,16 +113,12 @@ impl Readability {
|
||||||
for mut child_node in node_ref.get_child_nodes().into_iter() {
|
for mut child_node in node_ref.get_child_nodes().into_iter() {
|
||||||
if Self::is_phrasing_content(&child_node) {
|
if Self::is_phrasing_content(&child_node) {
|
||||||
if let Some(p) = p.as_mut() {
|
if let Some(p) = p.as_mut() {
|
||||||
|
child_node.unlink();
|
||||||
let _ = p.add_child(&mut child_node);
|
let _ = p.add_child(&mut child_node);
|
||||||
} else if !Util::is_whitespace(&child_node) {
|
} else if !Util::is_whitespace(&child_node) {
|
||||||
|
child_node.unlink();
|
||||||
let mut new_node = Node::new("p", None, &document)
|
let mut new_node = Node::new("p", None, &document)
|
||||||
.map_err(|()| FullTextParserError::Readability)?;
|
.map_err(|()| FullTextParserError::Readability)?;
|
||||||
node_ref
|
|
||||||
.replace_child_node(new_node.clone(), child_node.clone())
|
|
||||||
.map_err(|error| {
|
|
||||||
log::error!("{error}");
|
|
||||||
FullTextParserError::Readability
|
|
||||||
})?;
|
|
||||||
new_node.add_child(&mut child_node).map_err(|error| {
|
new_node.add_child(&mut child_node).map_err(|error| {
|
||||||
log::error!("{error}");
|
log::error!("{error}");
|
||||||
FullTextParserError::Readability
|
FullTextParserError::Readability
|
||||||
|
@ -247,6 +249,9 @@ impl Readability {
|
||||||
});
|
});
|
||||||
|
|
||||||
let top_candidates = candidates.into_iter().take(5).collect::<Vec<_>>();
|
let top_candidates = candidates.into_iter().take(5).collect::<Vec<_>>();
|
||||||
|
// for candidate in top_candidates.iter() {
|
||||||
|
// println!("candidate: {} {:?}", candidate.get_name(), candidate.get_attributes());
|
||||||
|
// }
|
||||||
let mut needed_to_create_top_candidate = false;
|
let mut needed_to_create_top_candidate = false;
|
||||||
let mut top_candidate = top_candidates.first().cloned().unwrap_or_else(|| {
|
let mut top_candidate = top_candidates.first().cloned().unwrap_or_else(|| {
|
||||||
// If we still have no top candidate, just use the body as a last resort.
|
// If we still have no top candidate, just use the body as a last resort.
|
||||||
|
@ -619,12 +624,8 @@ impl Readability {
|
||||||
|
|
||||||
is_text_node
|
is_text_node
|
||||||
|| constants::PHRASING_ELEMS.contains(&tag_name.as_str())
|
|| constants::PHRASING_ELEMS.contains(&tag_name.as_str())
|
||||||
|| (tag_name == "A" || tag_name == "DEL" || tag_name == "INS")
|
|| ((tag_name == "A" || tag_name == "DEL" || tag_name == "INS")
|
||||||
&& node
|
&& node.get_child_nodes().iter().all(Self::is_phrasing_content))
|
||||||
.get_child_nodes()
|
|
||||||
.iter()
|
|
||||||
.map(Self::is_phrasing_content)
|
|
||||||
.all(|val| val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a node with the readability object. Also checks the
|
// Initialize a node with the readability object. Also checks the
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
async fn run_test(name: &str) {
|
async fn run_test(name: &str) {
|
||||||
libxml::tree::node::set_node_rc_guard(3);
|
libxml::tree::node::set_node_rc_guard(4);
|
||||||
let _ = env_logger::builder().is_test(true).try_init();
|
let _ = env_logger::builder().is_test(true).try_init();
|
||||||
|
|
||||||
let empty_config = ConfigEntry::default();
|
let empty_config = ConfigEntry::default();
|
||||||
|
@ -43,22 +43,27 @@ async fn run_test(name: &str) {
|
||||||
article.document = Some(article_document);
|
article.document = Some(article_document);
|
||||||
let html = article.get_content().unwrap();
|
let html = article.get_content().unwrap();
|
||||||
|
|
||||||
|
//std::fs::write("expected.html", &html).unwrap();
|
||||||
|
|
||||||
let expected = std::fs::read_to_string(format!(
|
let expected = std::fs::read_to_string(format!(
|
||||||
"./resources/tests/readability/{name}/expected.html"
|
"./resources/tests/readability/{name}/expected.html"
|
||||||
))
|
))
|
||||||
.expect("Failed to read expected HTML");
|
.expect("Failed to read expected HTML");
|
||||||
|
|
||||||
//std::fs::write("expected.html", &html).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected, html);
|
assert_eq!(expected, html);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
#[tokio::test]
|
||||||
async fn test_001() {
|
async fn test_001() {
|
||||||
run_test("001").await
|
run_test("001").await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
#[tokio::test]
|
||||||
async fn test_002() {
|
async fn test_002() {
|
||||||
run_test("002").await
|
run_test("002").await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn webmd_1() {
|
||||||
|
run_test("webmd-1").await
|
||||||
|
}
|
||||||
|
|
|
@ -360,11 +360,11 @@ impl Util {
|
||||||
|
|
||||||
pub fn has_single_tag_inside_element(node: &Node, tag: &str) -> bool {
|
pub fn has_single_tag_inside_element(node: &Node, tag: &str) -> bool {
|
||||||
// There should be exactly 1 element child with given tag
|
// There should be exactly 1 element child with given tag
|
||||||
if node.get_child_nodes().len() == 1
|
if node.get_child_nodes().len() != 1
|
||||||
|| node
|
|| node
|
||||||
.get_child_nodes()
|
.get_child_nodes()
|
||||||
.first()
|
.first()
|
||||||
.map(|n| n.get_name().to_uppercase() == tag)
|
.map(|n| n.get_name().to_uppercase() != tag)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -438,8 +438,8 @@ impl Util {
|
||||||
|
|
||||||
// Determine whether element has any children block level elements.
|
// Determine whether element has any children block level elements.
|
||||||
pub fn has_child_block_element(node: &Node) -> bool {
|
pub fn has_child_block_element(node: &Node) -> bool {
|
||||||
node.get_child_elements().iter().any(|node| {
|
node.get_child_nodes().iter().any(|node| {
|
||||||
constants::DIV_TO_P_ELEMS.contains(node.get_name().as_str())
|
constants::DIV_TO_P_ELEMS.contains(node.get_name().to_uppercase().as_str())
|
||||||
|| Self::has_child_block_element(node)
|
|| Self::has_child_block_element(node)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue