--- title: Fetch の使用 slug: Web/API/Fetch_API/Using_Fetch tags: - API - BODY - Experimental - Fetch - Guide - HTTP - Promise - Response - request translation_of: Web/API/Fetch_API/Using_Fetch ---
{{DefaultAPISidebar("Fetch API")}}
Fetch API を利用すると、リクエストやレスポンスといった HTTP のパイプラインを構成する要素を操作できるようになります。また {{domxref("GlobalFetch.fetch","fetch()")}} メソッドを利用することで、非同期のネットワーク通信を簡単にわかりやすく記述できるようになります。
従来、このような機能は {{domxref("XMLHttpRequest")}} を使用して実現されてきました。 Fetch はそれのより良い代替となるもので、{{domxref("ServiceWorker_API", "サービスワーカー")}}のような他の技術から簡単に利用することができます。 Fetch は CORS や HTTP 拡張のような HTTP に関連する概念をまとめて定義する場所でもあります。
fetch
の仕様は jQuery.ajax()
とは主に二つの点で異なっています。
fetch()
から返される Promise は レスポンスが HTTP 404 や 500 を返して HTTP エラーステータスの場合でも拒否されません。代わりに (ok
ステータスが false にセットされて) 正常に解決し、拒否されるのはネットワークのエラーや、何かがリクエストの完了を妨げた場合のみです。fetch()
はサイトをまたぐクッキーをfetch
はサーバーとの間で cookies を送受信しないため、サイトがユーザーセッションの維持に頼っている場合は未認証のリクエストになります。クッキーを送るには、認証情報の init オプションを設定しておく必要があります。 (2017年8月25日に、既定の認証情報のポリシーが same-origin
に変更になり、 Firefox は 61.0b13 から変更しました。)基本的な fetch リクエストは、本当に簡単に設定できます。以下のコードを見てください。
fetch('http://example.com/movies.json') .then(response => response.json()) .then(data => console.log(data));
これはネットワーク越しに JSON ファイルを取得してコンソールに出力するスクリプトです。 fetch()
の最も簡単な使い方は 1 つの引数 — fetch で取得したいリソースへのパス — のみをとり、レスポンス ({{domxref("Response")}} オブジェクト) を含む promise を返します。
これはただの HTTP レスポンスであり、実際の JSON ではありません。 response オブジェクトから JSON を抽出するには、 {{domxref("Body.json","json()")}} メソッドを使用する必要があります。({{domxref("Body")}} のミックスインとして定義されていて、これは {{domxref("Request")}} と {{domxref("Response")}} の両オブジェクトに実装されています。)
メモ: Body ミックスインは本文の内容を他の mime タイプとして展開する似たようなメソッドを提供しています。詳細は {{anch("Body")}} の節をご覧ください。
Fetch リクエストは、検索したリソースからの指示よりも Content Security Policy の connect-src
ディレクティブによって制御されます。
fetch()
メソッドには 2 つ目の引数を適用することもできます。多数の設定をコントロールすることのできる init
オブジェクトです。
すべての設定可能なオプションや詳細な説明を見るには {{domxref("GlobalFetch.fetch","fetch()")}} を参照してください。
// POST メソッドの実装の例 async function postData(url = '', data = {}) { // 既定のオプションには * が付いています const response = await fetch(url, { method: 'POST', // *GET, POST, PUT, DELETE, etc. mode: 'cors', // no-cors, *cors, same-origin cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached credentials: 'same-origin', // include, *same-origin, omit headers: { 'Content-Type': 'application/json' // 'Content-Type': 'application/x-www-form-urlencoded', }, redirect: 'follow', // manual, *follow, error referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url body: JSON.stringify(data) // 本文のデータ型は "Content-Type" ヘッダーと一致する必要があります }) return response.json(); // レスポンスの JSON を解析 } postData('https://example.com/answer', { answer: 42 }) .then(data => { console.log(data); // `data.json()` の呼び出しで解釈された JSON データ });
なお、 mode: "no-cors"
はリクエスト中の限られた数のヘッダーにしか許可されていません。
Accept
Accept-Language
Content-Language
Content-Type
のうち、値が application/x-www-form-urlencoded
, multipart/form-data
, text/plain
のいずれかのものブラウザーに認証情報の入ったリクエストを送るようにするには、オリジン間の呼び出しであっても、 credentials: 'include'
を init
オブジェクトに追加して fetch()
メソッドに渡します。
fetch('https://example.com', { credentials: 'include' });
リクエスト URL が呼び出しスクリプトと同一オリジンの場合だけクレデンシャルを送りたい場合、credentials: 'same-origin'
を追加します。
// The calling script is on the origin 'https://example.com' fetch('https://example.com', { credentials: 'same-origin' });
この代わりにブラウザーがリクエストにクレデンシャルを含んでないことを保証するには、credentials: 'omit'
を使います。
fetch('https://example.com', { credentials: 'omit' })
{{domxref("GlobalFetch.fetch","fetch()")}} を使って JSON-エンコードしたデータを POST します。
const data = { username: 'example' }; fetch('https://example.com/profile', { method: 'POST', // or 'PUT' headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) .then(response => response.json()) .then(data => { console.log('Success:', data); }) .catch((error) => { console.error('Error:', error); });
ファイルは HTML <input type="file" />
input 要素や、 {{domxref("FormData.FormData","FormData()")}} や {{domxref("WindowOrWorkerGlobalScope/fetch","fetch()")}} を使ってアップロードできます。
const formData = new FormData(); const fileField = document.querySelector('input[type="file"]'); formData.append('username', 'abc123'); formData.append('avatar', fileField.files[0]); fetch('https://example.com/profile/avatar', { method: 'PUT', body: formData }) .then(response => response.json()) .then(result => { console.log('Success:', result); }) .catch(error => { console.error('Error:', error); });
HTML の <input type="file" multiple />
入力欄と {{domxref("FormData.FormData","FormData()")}} と {{domxref("GlobalFetch.fetch","fetch()")}} を使用してファイルをアップロードすることができます。
const formData = new FormData(); const photos = document.querySelector('input[type="file"][multiple]'); formData.append('title', 'My Vegas Vacation'); for (let i = 0; i < photos.files.length; i++) { formData.append('photos', photos.files[i]); } fetch('https://example.com/posts', { method: 'POST', body: formData, }) .then(response => response.json()) .then(result => { console.log('Success:', result); }) .catch(error => { console.error('Error:', error); });
レスポンスから読み込まれるチャンクは、行の境界できれいに分割されておらず、文字列ではなく Uint8Arrays になっています。テキストファイルをフェッチして一行ずつ処理したい場合、これらの複雑な処理を行うのはあなた次第です。次の例は、行イテレータを作成することでこれを行う方法の一つを示しています (簡単にするため、テキストは UTF-8 であると仮定しており、フェッチエラーは処理していません)。
async function* makeTextFileLineIterator(fileURL) { const utf8Decoder = new TextDecoder('utf-8'); const response = await fetch(fileURL); const reader = response.body.getReader(); let { value: chunk, done: readerDone } = await reader.read(); chunk = chunk ? utf8Decoder.decode(chunk) : ''; const re = /\n|\r|\r\n/gm; let startIndex = 0; let result; for (;;) { let result = re.exec(chunk); if (!result) { if (readerDone) { break; } let remainder = chunk.substr(startIndex); ({ value: chunk, done: readerDone } = await reader.read()); chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : ''); startIndex = re.lastIndex = 0; continue; } yield chunk.substring(startIndex, result.index); startIndex = re.lastIndex; } if (startIndex < chunk.length) { // last line didn't end in a newline char yield chunk.substr(startIndex); } } async function run() { for await (let line of makeTextFileLineIterator(urlOfFile)) { processLine(line); } } run();
ネットワークエラーに遭遇すると {{domxref("GlobalFetch.fetch","fetch()")}} promise は {{jsxref("TypeError")}} を返して reject 状態になります。サーバー側の CORS が適切に設定されていない場合も同様です(アクセス権の問題ですけどね) — 一方で例えば 404 はネットワークエラーを構成しません。fetch() が成功したかどうかの明確な判定をするには、プロミスが解決されて、{{domxref("Response.ok")}} プロパティが true になっているかなどを確認します。次のようなコードになるでしょう。
fetch('flowers.jpg') .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.blob(); }) .then(myBlob => { myImage.src = URL.createObjectURL(myBlob); }) .catch(error => { console.error('There has been a problem with your fetch operation:', error); });
fetch()
を呼ぶときにリクエストしたいリソースへのパスを渡す代わりに、{{domxref("Request.Request","Request()")}} コンストラクターを使用して Request オブジェクトを作成して fetch()
メソッドの引数として渡すこともできます。
const myHeaders = new Headers(); const myRequest = new Request('flowers.jpg', { method: 'GET', headers: myHeaders, mode: 'cors', cache: 'default', }); fetch(myRequest) .then(response => response.blob()) .then(myBlob => { myImage.src = URL.createObjectURL(myBlob); });
fetch()
メソッドの引数と全く同じ引数を Request()
に適用させることができます。また、 request オブジェクトのコピーを作成するためにすでに存在する request オブジェクトを渡すこともできます。
const anotherRequest = new Request(myRequest, myInit);
これは、リクエストとレスポンスの本文を一つだけ使用するのでとても有用です。必要であれば、init
オプションを変化させながらリクエスト / レスポンスを再利用できるようにコピーします。コピーは body が読まれる前でなければならず、コピーの中の body を読むとオリジナルのリクエストも既読にマークされます。
メモ: {{domxref("Request.clone","clone()")}} メソッドを利用してコピーを生成することもできます。これには、ほかのコピーメソッドと若干異なる意味があります — 古いリクエストの body がすでに読み込まれていた場合、前者は失敗しますが、clone()
は失敗しません (レスポンスでも同じです)。
{{domxref("Headers")}} インターフェースでは、 {{domxref("Headers.Headers","Headers()")}} コンストラクターを使用して、ヘッダーオブジェクトを作成することができます。ヘッダーオブジェクトはシンプルな複数の名前と値の Map です。
const content = 'Hello World'; const myHeaders = new Headers(); myHeaders.append('Content-Type', 'text/plain'); myHeaders.append('Content-Length', content.length.toString()); myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');
同じことはコンストラクターに配列の配列かオブジェクトリテラルを渡すことで達成できます。
const myHeaders = new Headers({ 'Content-Type': 'text/plain', 'Content-Length': content.length.toString(), 'X-Custom-Header': 'ProcessThisImmediately' });
ヘッダーの中身を見たり、検索することができます。
console.log(myHeaders.has('Content-Type')); // true console.log(myHeaders.has('Set-Cookie')); // false myHeaders.set('Content-Type', 'text/html'); myHeaders.append('X-Custom-Header', 'AnotherValue'); console.log(myHeaders.get('Content-Length')); // 11 console.log(myHeaders.get('X-Custom-Header')); // ['ProcessThisImmediately', 'AnotherValue'] myHeaders.delete('X-Custom-Header'); console.log(myHeaders.get('X-Custom-Header')); // [ ]
いくつかの操作は {{domxref("ServiceWorker_API","ServiceWorkers")}} でしか役立ちませんが、ヘッダーを操作するためのより良い API を提供しています。
Headers のメソッドはすべて、有効な HTTP ヘッダーではない名前が渡されたとき TypeError を投げます。 immutable ガード (下記参照) がかかっている場合も、 TypeError を投げます。もしくはエラーを投げずに失敗します。例を見てください。
const myResponse = Response.error(); try { myResponse.headers.set('Origin', 'http://mybank.com'); } catch (e) { console.log("銀行のふりをしないで下さい!"); }
ヘッダーの良い使用方法としては、以下のように、処理を行う前に、コンテンツタイプが正しいかどうか判定する等の使い方があります。
fetch(myRequest) .then(response => { const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { throw new TypeError("Oops, we haven't got JSON!"); } return response.json(); }) .then(data => { /* process your data further */ }) .catch(error => console.error(error));
ヘッダーは、リクエストで送信でき、レスポンスで受信できます。また、どの情報が変更できる(または、すべき)かといったさまざまな制限があります。そのため、ヘッダーは guard プロパティを持っています。これはリクエストやレスポンスに含まれませんが、ヘッダーオブジェクトでできる変更操作に影響を与えます。
設定できるガード値には以下のものがあります。
none
: 既定値です。request
: リクエスト ({{domxref("Request.headers")}}) で使用できる値のみにヘッダーを保護する。request-no-cors
: {{domxref("Request.mode")}} no-cors
で生成されたリクエスト ({{domxref("Request.headers")}}) で使用できる値のみにヘッダーを保護する。response
: レスポンス ({{domxref("Response.headers")}}) で使用できる値のみにヘッダーを保護する。immutable
: 主に ServiceWorker で使用されます。ヘッダーを読み取り専用にします。メモ: request
のガードされたヘッダーの Content-Length
ヘッダーは追加や変更できない可能性があります。同様に、レスポンスヘッダに Set-Cookie
を挿入することはできません。ServiceWorker は、同期レスポンスを経由してクッキーを設定できません。
すでに見てきたように, {{domxref("Response")}} インスタンスは、 fetch()
プロミスが解決(resolve)されたときに返り値として渡されます。
下記はどんな response オブジェクトでも共通で使用できる response プロパティです。
Response オブジェクトは JavaScript で動的に作ることもできます。これは {{domxref("ServiceWorker_API", "ServiceWorkers")}} 内において非常に役立ちます。例えばリクエストを受け取ったときに {{domxref("FetchEvent.respondWith","respondWith()")}} メソッドによってカスタマイズされたレスポンスを返すようなときに役立ちます。
const myBody = new Blob(); addEventListener('fetch', function(event) { // ServiceWorker intercepting a fetch event.respondWith( new Response(myBody, { headers: { 'Content-Type': 'text/plain' } }) ); });
{{domxref("Response.Response","Response()")}} コンストラクターはオプションとして 2 つの引数をとることができます — レスポンス本文と初期化オブジェクトです。 ({{domxref("Request.Request","Request()")}} が受け取れるものと似ています。)
メモ: 静的メソッド {{domxref("Response.error","error()")}} は単純にエラーレスポンスを返します。同様に {{domxref("Response.redirect","redirect()")}} メソッドも 指定した URL にリダイレクトするレスポンスを返します。これらはサービスワーカーにのみ関連しています。
リクエストもレスポンスもボディを持っています。body は以下のタイプのいずれかのインスタンスです。
{{domxref("Body")}} ミックスインは {{domxref("Request")}} や{{domxref("Response")}} に実装されていて、コンテンツを抜き出すために以下のメソッドが定義されています。これらはすべて最終的に実際の中身で解決されるプロミスを返します。
これらは非テキストデータを XHR よりはるかに楽に扱うことができます。
Request 本文は、body パラメータを渡すことによって設定することができます。
const form = new FormData(document.getElementById('login-form')); fetch('/login', { method: 'POST', body: form });
Request や Response (と fetch()
関数の拡張) は自動的にコンテンツタイプを決定しようとします。Request もまた、指定されていなければ自動で Content-Type ヘッダーを設定しようとします。
Fetch API が利用できるかどうかは、{{domxref("Headers")}}、{{domxref("Request")}}、{{domxref("Response")}}、{{domxref("GlobalFetch.fetch","fetch()")}} のいずれかが {{domxref("Window")}} もしくは {{domxref("Worker")}} のスコープで参照できるかどうかによって判断できます。判断を行っている例は次のようになります。
if (window.fetch) { // ここで fetch リクエストを実行 } else { // XMLHttpRequest で何か実行する? }
Fetch がサポートされていないブラウザーを使うため、非サポートブラウザー用の機能を再生成する Fetch Polyfill が利用できます。
仕様書 | 状態 | 備考 |
---|---|---|
{{SpecName('Fetch')}} | {{Spec2('Fetch')}} | 初回定義 |
{{Compat("api.WindowOrWorkerGlobalScope.fetch")}}