Codelab ini adalah bagian dari kursus pelatihan Developing Progressive Web Apps, yang dikembangkan oleh tim Pelatihan Google Developers. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan.
Untuk mengetahui detail lengkap tentang kursus ini, lihat Ringkasan Pengembangan Progressive Web App.
Pengantar
Lab ini memandu Anda menggunakan Fetch API, antarmuka sederhana untuk mengambil resource, dan peningkatan dari XMLHttpRequest API.
Yang akan Anda pelajari
- Cara menggunakan Fetch API untuk meminta resource
- Cara membuat permintaan GET, HEAD, dan POST dengan pengambilan
- Cara membaca & menyetel header kustom
- Penggunaan dan batasan CORS
Yang perlu Anda ketahui
- JavaScript dan HTML dasar
- Memahami konsep dan sintaksis dasar Promises ES2015
Yang akan Anda butuhkan
- Komputer dengan akses terminal/shell
- Koneksi ke internet
- Browser yang mendukung Fetch
- Editor teks
- Node dan npm
Catatan: Meskipun Fetch API saat ini tidak didukung di semua browser, ada polyfill.
Download atau clone repositori pwa-training-labs dari github dan instal Node.js versi LTS, jika diperlukan.
Buka command line komputer Anda. Buka direktori fetch-api-lab/app/
dan mulai server pengembangan lokal:
cd fetch-api-lab/app npm install node server.js
Anda dapat menghentikan server kapan saja dengan Ctrl-c
.
Buka browser Anda, lalu buka localhost:8081/
. Anda akan melihat halaman dengan tombol untuk membuat permintaan (tombol tersebut belum berfungsi).
Catatan: Batalkan pendaftaran pekerja layanan dan hapus semua cache pekerja layanan untuk localhost agar tidak mengganggu lab. Di Chrome DevTools, Anda dapat melakukannya dengan mengklik Hapus data situs dari bagian Hapus penyimpanan di tab Aplikasi.
Buka folder fetch-api-lab/app/
di editor teks pilihan Anda. Folder app/
adalah tempat Anda akan membangun lab.
Folder ini berisi:
echo-servers/
berisi file yang digunakan untuk menjalankan server pengujianexamples/
berisi contoh resource yang kita gunakan dalam bereksperimen dengan pengambilanjs/main.js
adalah JavaScript utama untuk aplikasi, dan di sinilah Anda akan menulis semua kodeindex.html
adalah halaman HTML utama untuk situs/aplikasi contoh kamipackage-lock.json
danpackage.json
adalah file konfigurasi untuk dependensi server pengembangan dan server echo kamiserver.js
adalah server pengembangan node
Fetch API memiliki antarmuka yang relatif sederhana. Bagian ini menjelaskan cara menulis permintaan HTTP dasar menggunakan fetch.
Mengambil file JSON
Di js/main.js
, tombol Fetch JSON aplikasi dilampirkan ke fungsi fetchJSON
.
Perbarui fungsi fetchJSON
untuk meminta file examples/animals.json
dan mencatat respons:
function fetchJSON() {
fetch('examples/animals.json')
.then(logResult)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Fetch JSON. Konsol akan mencatat respons pengambilan data.
Penjelasan
Metode fetch
menerima jalur untuk resource yang ingin kita ambil sebagai parameter, dalam hal ini examples/animals.json
. fetch
menampilkan promise yang diselesaikan ke objek Respons. Jika promise diselesaikan, respons akan diteruskan ke fungsi logResult
. Jika promise ditolak, catch
akan mengambil alih dan error akan diteruskan ke fungsi logError
.
Objek respons merepresentasikan respons terhadap permintaan. Objek ini berisi isi respons serta properti dan metode yang berguna.
Menguji respons tidak valid
Periksa respons yang dicatat ke dalam log di konsol. Perhatikan nilai properti status
, url
, dan ok
.
Ganti resource examples/animals.json
di fetchJSON
dengan examples/non-existent.json
. Fungsi fetchJSON
yang diupdate kini akan terlihat seperti:
function fetchJSON() {
fetch('examples/non-existent.json')
.then(logResult)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Fetch JSON lagi untuk mencoba mengambil resource yang tidak ada ini.
Perhatikan bahwa pengambilan berhasil diselesaikan, dan tidak memicu pemblokiran catch
. Sekarang temukan properti status
, URL
, dan ok
dari respons baru.
Nilai untuk kedua file tersebut harus berbeda (apakah Anda mengerti alasannya?). Jika Anda mendapatkan error konsol, apakah nilai tersebut cocok dengan konteks error?
Penjelasan
Mengapa respons yang gagal tidak mengaktifkan blok catch
? Ini adalah catatan penting untuk pengambilan dan janji—respons buruk (seperti 404) tetap diselesaikan. Fetch promise hanya ditolak jika permintaan tidak dapat diselesaikan, jadi Anda harus selalu memeriksa validitas respons. Kita akan memvalidasi respons di bagian berikutnya.
Untuk informasi selengkapnya
Memeriksa validitas respons
Kita perlu memperbarui kode untuk memeriksa validitas respons.
Di main.js
, tambahkan fungsi untuk memvalidasi respons:
function validateResponse(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
Kemudian, ganti fetchJSON
dengan kode berikut:
function fetchJSON() {
fetch('examples/non-existent.json')
.then(validateResponse)
.then(logResult)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Fetch JSON. Periksa konsol. Sekarang, respons untuk examples/non-existent.json
akan memicu blok catch
.
Ganti examples/non-existent.json
di fungsi fetchJSON
dengan examples/animals.json
asli. Fungsi yang diupdate kini akan terlihat seperti:
function fetchJSON() {
fetch('examples/animals.json')
.then(validateResponse)
.then(logResult)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Fetch JSON. Anda akan melihat bahwa respons berhasil dicatat seperti sebelumnya.
Penjelasan
Setelah menambahkan pemeriksaan validateResponse
, respons buruk (seperti 404) akan memunculkan error dan catch
akan mengambil alih. Hal ini memungkinkan kami menangani respons yang gagal dan mencegah respons yang tidak terduga menyebar ke bawah rantai pengambilan data.
Baca responsnya
Respons pengambilan data ditampilkan sebagai ReadableStreams (spesifikasi stream) dan harus dibaca untuk mengakses isi respons. Objek respons memiliki metode untuk melakukannya.
Di main.js
, tambahkan fungsi readResponseAsJSON
dengan kode berikut:
function readResponseAsJSON(response) {
return response.json();
}
Kemudian, ganti fungsi fetchJSON
dengan kode berikut:
function fetchJSON() {
fetch('examples/animals.json') // 1
.then(validateResponse) // 2
.then(readResponseAsJSON) // 3
.then(logResult) // 4
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Fetch JSON. Periksa konsol untuk melihat bahwa JSON dari examples/animals.json
sedang dicatat (bukan objek Respons).
Penjelasan
Mari kita tinjau apa yang terjadi.
Langkah 1. Pengambilan dipanggil pada resource, examples/animals.json
. Fetch menampilkan promise yang diselesaikan ke objek Respons. Saat promise di-resolve, objek respons diteruskan ke validateResponse
.
Langkah 2. validateResponse
memeriksa apakah respons valid (apakah responsnya 200?). Jika tidak, error akan ditampilkan, melewati blok then
lainnya dan memicu blok catch
. Hal ini sangat penting. Tanpa pemeriksaan ini, respons buruk akan diteruskan ke bawah dan dapat merusak kode selanjutnya yang mungkin mengandalkan penerimaan respons yang valid. Jika respons valid, respons tersebut diteruskan ke readResponseAsJSON
.
Langkah 3. readResponseAsJSON
membaca isi respons menggunakan metode Response.json(). Metode ini menampilkan promise yang diselesaikan ke JSON. Setelah promise ini diselesaikan, data JSON akan diteruskan ke logResult
. (Jika promise dari response.json()
ditolak, blok catch
akan dipicu.)
Langkah 4. Terakhir, data JSON dari permintaan asli ke examples/animals.json
dicatat oleh logResult
.
Untuk informasi selengkapnya
Pengambilan data tidak terbatas pada JSON. Dalam contoh ini, kita akan mengambil gambar dan menambahkannya ke halaman.
Di main.js
, tulis fungsi showImage
dengan kode berikut:
function showImage(responseAsBlob) {
const container = document.getElementById('img-container');
const imgElem = document.createElement('img');
container.appendChild(imgElem);
const imgUrl = URL.createObjectURL(responseAsBlob);
imgElem.src = imgUrl;
}
Kemudian, tambahkan fungsi readResponseAsBlob
yang membaca respons sebagai Blob:
function readResponseAsBlob(response) {
return response.blob();
}
Perbarui fungsi fetchImage
dengan kode berikut:
function fetchImage() {
fetch('examples/fetching.jpg')
.then(validateResponse)
.then(readResponseAsBlob)
.then(showImage)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Ambil gambar. Anda akan melihat lucu mengambil tongkat di halaman (ini adalah lelucon mengambil).
Penjelasan
Dalam contoh ini, gambar sedang diambil, examples/fetching.jpg
. Seperti pada latihan sebelumnya, respons divalidasi dengan validateResponse
. Respons kemudian dibaca sebagai Blob (bukan JSON seperti di bagian sebelumnya). Elemen gambar dibuat dan ditambahkan ke halaman, dan atribut src
gambar ditetapkan ke URL data yang merepresentasikan Blob.
Catatan: Metode createObjectURL()
objek URL digunakan untuk membuat URL data yang merepresentasikan Blob. Hal ini penting untuk diperhatikan. Anda tidak dapat menyetel sumber gambar langsung ke Blob. Blob harus dikonversi menjadi URL data.
Untuk informasi selengkapnya
Bagian ini adalah tantangan opsional.
Update fungsi fetchText
menjadi
- ambil
/examples/words.txt
- memvalidasi respons dengan
validateResponse
- membaca respons sebagai teks (petunjuk: lihat Response.text())
- dan menampilkan teks di halaman
Anda dapat menggunakan fungsi showText
ini sebagai helper untuk menampilkan teks akhir:
function showText(responseAsText) {
const message = document.getElementById('message');
message.textContent = responseAsText;
}
Simpan skrip dan muat ulang halaman. Klik Ambil teks. Jika telah menerapkan fetchText
dengan benar, Anda akan melihat teks yang ditambahkan di halaman.
Catatan: Meskipun Anda mungkin tergoda untuk mengambil HTML dan menambahkannya menggunakan atribut innerHTML
, berhati-hatilah. Hal ini dapat membuat situs Anda rentan terhadap serangan pembuatan skrip lintas situs.
Untuk informasi selengkapnya
Secara default, pengambilan menggunakan metode GET, yang mengambil resource tertentu. Namun, pengambilan data juga dapat menggunakan metode HTTP lainnya.
Membuat permintaan HEAD
Ganti fungsi headRequest
dengan kode berikut:
function headRequest() {
fetch('examples/words.txt', {
method: 'HEAD'
})
.then(validateResponse)
.then(readResponseAsText)
.then(logResult)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik Permintaan HEAD. Perhatikan bahwa konten teks yang dicatat kosong.
Penjelasan
Metode fetch
dapat menerima parameter opsional kedua, init
. Parameter ini memungkinkan konfigurasi permintaan pengambilan, seperti metode permintaan, mode cache, kredensial, dan lainnya.
Dalam contoh ini, kita menetapkan metode permintaan pengambilan ke HEAD menggunakan parameter init
. Permintaan HEAD sama seperti permintaan GET, kecuali isi responsnya kosong. Jenis permintaan ini dapat digunakan saat Anda hanya menginginkan metadata tentang file, tetapi tidak perlu mentransfer semua data file.
Opsional: Temukan ukuran resource
Mari kita lihat Header respons pengambilan untuk examples/words.txt
guna menentukan ukuran file.
Perbarui fungsi headRequest
untuk mencatat properti content-length
dari respons headers
(petunjuk: lihat dokumentasi header dan metode get).
Setelah memperbarui kode, simpan file dan muat ulang halaman. Klik Permintaan HEAD. Konsol harus mencatat ukuran (dalam byte) examples/words.txt
.
Penjelasan
Dalam contoh ini, metode HEAD digunakan untuk meminta ukuran (dalam byte) resource (yang ditampilkan di header content-length
) tanpa benar-benar memuat resource itu sendiri. Dalam praktiknya, hal ini dapat digunakan untuk menentukan apakah resource lengkap harus diminta (atau bahkan cara memintanya).
Opsional: Cari tahu ukuran examples/words.txt
menggunakan metode lain dan pastikan ukurannya cocok dengan nilai dari header respons (Anda dapat mencari cara melakukannya untuk sistem operasi tertentu—poin bonus untuk menggunakan command line).
Untuk informasi selengkapnya
Fetch juga dapat mengirim data dengan permintaan POST.
Menyiapkan server echo
Untuk contoh ini, Anda perlu menjalankan server echo. Dari direktori fetch-api-lab/app/
, jalankan perintah berikut (jika command line Anda diblokir oleh server localhost:8081
, buka jendela atau tab command line baru):
node echo-servers/cors-server.js
Perintah ini memulai server sederhana di localhost:5000/
yang mengembalikan permintaan yang dikirimkan kepadanya.
Anda dapat menghentikan server ini kapan saja dengan ctrl+c
.
Buat permintaan POST
Ganti fungsi postRequest
dengan kode berikut (pastikan Anda telah menentukan fungsi showText
dari bagian 4 jika Anda belum menyelesaikan bagian tersebut):
function postRequest() {
fetch('http://localhost:5000/', {
method: 'POST',
body: 'name=david&message=hello'
})
.then(validateResponse)
.then(readResponseAsText)
.then(showText)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Klik POST request. Amati permintaan yang dikirimkan dan ditampilkan di halaman. Objek ini harus berisi nama dan pesan (perhatikan bahwa kita belum mendapatkan data dari formulir).
Penjelasan
Untuk membuat permintaan POST dengan pengambilan, kita menggunakan parameter init
untuk menentukan metode (mirip dengan cara kita menetapkan metode HEAD di bagian sebelumnya). Di sini juga kita menetapkan body permintaan, dalam hal ini berupa string sederhana. Isinya adalah data yang ingin kita kirim.
Catatan: Dalam produksi, jangan lupa untuk selalu mengenkripsi data pengguna yang sensitif.
Saat data dikirim sebagai permintaan POST ke localhost:5000/
, permintaan akan dikirim kembali sebagai respons. Respons kemudian divalidasi dengan validateResponse
, dibaca sebagai teks, dan ditampilkan di halaman.
Pada praktiknya, server ini akan merepresentasikan API pihak ketiga.
Opsional: Menggunakan antarmuka FormData
Anda dapat menggunakan antarmuka FormData untuk mengambil data dari formulir dengan mudah.
Dalam fungsi postRequest
, buat instance objek FormData
baru dari elemen formulir msg-form
:
const formData = new FormData(document.getElementById('msg-form'));
Kemudian, ganti nilai parameter body
dengan variabel formData
.
Simpan skrip dan muat ulang halaman. Isi formulir (bidang Nama dan Pesan) di halaman, lalu klik permintaan POST. Amati konten formulir yang ditampilkan di halaman.
Penjelasan
Konstruktor FormData
dapat menerima form
HTML, dan membuat objek FormData
. Objek ini diisi dengan kunci dan nilai formulir.
Untuk informasi selengkapnya
Mulai server echo non-cors
Hentikan server echo sebelumnya (dengan menekan ctrl+c
dari command line) dan mulai server echo baru dari direktori fetch-lab-api/app/
dengan menjalankan perintah berikut:
node echo-servers/no-cors-server.js
Perintah ini menyiapkan server echo sederhana lainnya, kali ini di localhost:5001/
. Namun, server ini tidak dikonfigurasi untuk menerima permintaan lintas asal.
Mengambil dari server baru
Sekarang setelah server baru berjalan di localhost:5001/
, kita dapat mengirim permintaan pengambilan ke server tersebut.
Perbarui fungsi postRequest
untuk mengambil data dari localhost:5001/
, bukan localhost:5000/
. Setelah Anda memperbarui kode, simpan file, muat ulang halaman, lalu klik POST Request.
Anda akan mendapatkan error di konsol yang menunjukkan bahwa permintaan lintas origin diblokir karena header Access-Control-Allow-Origin
CORS tidak ada.
Perbarui fetch
dalam fungsi postRequest
dengan kode berikut, yang menggunakan mode no-cors (seperti yang disarankan oleh log error), dan menghapus panggilan ke validateResponse
dan readResponseAsText
(lihat penjelasan di bawah):
function postRequest() {
const formData = new FormData(document.getElementById('msg-form'));
fetch('http://localhost:5001/', {
method: 'POST',
body: formData,
mode: 'no-cors'
})
.then(logResult)
.catch(logError);
}
Simpan skrip dan muat ulang halaman. Kemudian, isi formulir pesan dan klik POST Request.
Amati objek respons yang dicatat di konsol.
Penjelasan
Fetch (dan XMLHttpRequest) mengikuti kebijakan origin yang sama. Artinya, browser membatasi permintaan HTTP lintas origin dari dalam skrip. Permintaan lintas origin terjadi saat satu domain (misalnya http://foo.com/
) meminta resource dari domain terpisah (misalnya http://bar.com/
).
Catatan: Pembatasan permintaan lintas origin sering kali menimbulkan kebingungan. Banyak resource seperti gambar, stylesheet, dan skrip diambil di seluruh domain (yaitu, lintas origin). Namun, ini adalah pengecualian untuk kebijakan origin yang sama. Permintaan lintas origin masih dibatasi dari dalam skrip.
Karena server aplikasi kita memiliki nomor port yang berbeda dengan kedua server echo, permintaan ke salah satu server echo dianggap sebagai permintaan lintas origin. Namun, server echo pertama yang berjalan di localhost:5000/
dikonfigurasi untuk mendukung CORS (Anda dapat membuka echo-servers/cors-server.js
dan memeriksa konfigurasi). Server echo baru, yang berjalan di localhost:5001/
, tidak (itulah sebabnya kita mendapatkan error).
Penggunaan mode: no-cors
memungkinkan pengambilan respons buram. Hal ini memungkinkan penggunaan untuk mendapatkan respons, tetapi mencegah akses ke respons dengan JavaScript (itulah sebabnya kita tidak dapat menggunakan validateResponse
, readResponseAsText
, atau showResponse
). Respons masih dapat digunakan oleh API lain atau di-cache oleh service worker.
Mengubah header permintaan
Fetch juga mendukung pengubahan header permintaan. Hentikan server echo localhost:5001
(tanpa CORS) dan mulai ulang server echo localhost:5000
(CORS) dari bagian 6:
node echo-servers/cors-server.js
Pulihkan fungsi postRequest
versi sebelumnya yang mengambil data dari localhost:5000/
:
function postRequest() {
const formData = new FormData(document.getElementById('msg-form'));
fetch('http://localhost:5000/', {
method: 'POST',
body: formData
})
.then(validateResponse)
.then(readResponseAsText)
.then(showText)
.catch(logError);
}
Sekarang gunakan Header interface untuk membuat objek Header di dalam fungsi postRequest
yang disebut messageHeaders
dengan header Content-Type
sama dengan application/json
.
Kemudian, tetapkan properti headers
objek init
menjadi variabel messageHeaders
.
Perbarui properti body
menjadi objek JSON yang di-stringifikasi, seperti:
JSON.stringify({ lab: 'fetch', status: 'fun' })
Setelah memperbarui kode, simpan file dan muat ulang halaman. Kemudian, klik POST Request.
Perhatikan bahwa permintaan yang di-echo sekarang memiliki Content-Type
application/json
(berbeda dengan multipart/form-data
seperti sebelumnya).
Sekarang tambahkan header Content-Length
kustom ke objek messageHeaders
dan beri permintaan ukuran arbitrer.
Setelah Anda memperbarui kode, simpan file, muat ulang halaman, dan klik POST Request. Perhatikan bahwa header ini tidak diubah dalam permintaan yang di-echo.
Penjelasan
Header interface memungkinkan pembuatan dan modifikasi objek Header. Beberapa header, seperti Content-Type
, dapat diubah dengan pengambilan. Yang lain, seperti Content-Length
, dilindungi dan tidak dapat diubah (karena alasan keamanan).
Menetapkan header permintaan kustom
Fetch mendukung penetapan header kustom.
Hapus header Content-Length
dari objek messageHeaders
dalam fungsi postRequest
. Tambahkan header kustom X-Custom
dengan nilai arbitrer (misalnya 'X-CUSTOM': 'hello world'
).
Simpan skrip, muat ulang halaman, lalu klik POST Request.
Anda akan melihat bahwa permintaan yang di-echo memiliki properti X-Custom
yang Anda tambahkan.
Sekarang tambahkan header Y-Custom
ke objek Headers. Simpan skrip, muat ulang halaman, lalu klik POST Request.
Anda akan mendapatkan error yang mirip dengan ini di konsol:
Fetch API cannot load http://localhost:5000/. Request header field y-custom is not allowed by Access-Control-Allow-Headers in preflight response.
Penjelasan
Seperti permintaan lintas asal, header kustom harus didukung oleh server tempat resource diminta. Dalam contoh ini, server echo kami dikonfigurasi untuk menerima header X-Custom
, tetapi tidak menerima header Y-Custom
(Anda dapat membuka echo-servers/cors-server.js
dan mencari Access-Control-Allow-Headers
untuk melihatnya sendiri). Setiap kali header kustom ditetapkan, browser akan melakukan pemeriksaan preflight. Artinya, browser terlebih dahulu mengirimkan permintaan OPTIONS ke server, untuk menentukan metode dan header HTTP apa yang diizinkan oleh server. Jika server dikonfigurasi untuk menerima metode dan header permintaan asli, maka permintaan akan dikirim, jika tidak, error akan ditampilkan.
Untuk informasi selengkapnya
Kode solusi
Untuk mendapatkan salinan kode yang berfungsi, buka folder solution.
Sekarang Anda tahu cara menggunakan Fetch API.
Resource
Untuk melihat semua codelab dalam kursus pelatihan PWA, lihat codelab Selamat datang untuk kursus ini.