Fetch trong Javascript
Một trong những tác vụ không thể thiếu trong bất kỳ trang web nào, đó là gửi yêu cầu (network equests) đến máy chủ, tải xuống dữ liệu mới để cập nhật trang web lúc cần thiết. Các trường hợp ta cần dùng đến network request là:
- Gửi thông tin đăng ký/đăng nhập của người dùng
- Tải xuống thông tin cá nhân của người dùng
- Nhận những thông tin cập nhật từ máy chủ và làm mới trang web
- Gửi đơn hàng trong một trang thương mại điện tử
- v/v
Một điều quan trọng hơn, với sự hỗ trợ của những công cụ và API hiện đại, việc giao tiếp với máy chủ hiện tại đã có thể thực hiện mà không cần phải tải lại trang web. Khi tìm hiểu về nội dung này, bạn có thể tìm được kết quả liên quan với từ khoá AJAX
(Asynchronous JavaScript And XML).
Như đã đề cập, có rất nhiều cách để một trang web có thể giao tiếp với máy chủ. Ở bài này, chúng ta sẽ tìm hiểu về một API đã được tích hợp sẵn vào Javascript, đó là fetch()
. API này được thêm vào Javascript cách đây không lâu và có thể sẽ không hoạt động trên những trình duyệt phiên bản cũ. Điều này cũng không qua nghiệm trọng, vì hầu hết các trình duyệt đời mới đều hỗ trợ fetch()
rất tốt.
fetch() cơ bản
let promise = fetch(url, [options])
fetch()
đươc dùng với hay parameter chính:
url
– địa chỉ mà ta muốn gọi đếnoptions
– các thông số của request: method, headers, …
Khi không có thông số nào được gửi kèm trong options
, Javascript sẽ hiểu đó là một yêu cầu GET
, và sẽ tải xuống các dữ liệu được trả về từ địa chỉ url
. Khi được gọi, trình duyệt sẽ ngay bắt đầu gửi đi yêu cầu đến máy chủ, và trả về một promise. Ta sẽ sẽ xử lý promise này để có được kết quả.
Thông thường, ta cần hai bước để xử lý một fetch()
request:
Đầu tiên, fetch
sẽ trả về một promise
kết quả của là một object thuộc lớp Response
, ngay khi máy chủ bắt đầu gửi headers cho request.
Ở giai đoạn này, ta có thể kiểm tra trang thái HTTP và máy chủ trả về để biết request có thành công hay không, hoặc kiểm tra headers của kết quả. Lúc này, ta chưa thể xem được dữ liệu được trả về.
Trong trường hợp fetch()
gặp lỗi hoặc không thể gửi request đến máy chủ, promise sẽ bị từ chối (rejects). Thông thường fetch()
không thể gửi yêu cầu trong trường hợp sai địa chỉ, hoặc lỗi mạng. Một lưu ý là, các trạng thái ‘không bình thường’ trong kết quả trả về sẽ không được tính là lỗi, ví dụ status code là 400 hay 500.
Để kiểm tra trạng thái của request, ta có thể dùng hai thuộc tính của object trả về trong promise:
- status – HTTP status code:, ví dụ 200
- ok – có dạng boolean, giá trị là
true
nếu HTTP status từ 200 – 299.
let response = await fetch(url);
if (response.ok) { // Nếu HTTP status từ 200 - 299
// Xử lý kết quả trả về
} else {
alert("HTTP-Error: " + response.status);
}
Bước hai, ta sẽ dùng một số method trong object được trả về từ promise để lấy dữ liệu gửi về từ máy chủ
Ta có thể dùng các method sau (đây là các method của class Response) để lấy dữ liệu gửi về từ máy chủ:
response.text()
– lấy dữ liệu dưới dạng textresponse.json()
– lấy dữ liệu dưới dạng JSONresponse.formData()
– lấy dữ liệu dưới dạngFormData
response.blob()
– lấy dữ liệu dưới dạngBlob
(dữ liệu nhị phân)response.arrayBuffer()
– lấy dữ liệu dưới dạngArrayBuffer
- Ngoài các method trên, ta có thể dùng
response.body
– là mộtReadableStream
, dữ liệu sẽ được truyền về theo từng gói nhỏ (chunk-by-chunk).
Các method kết trên đều có dạng promised-based
, ta sẽ cần dùng từ khoá await
hoặc cấu trúc promise
đơn thuần.
Ví dụ, để nhận kết quả dạng JSON khi gửi yêu cầu đến GitHub:
let url = 'https://api.github.com/repos/microsoft/TypeScript/commits';
let response = await fetch(url);
let commits = await response.json(); // lấy dữ liệu trả về dưới dạng JSON với await
alert(commits[0].author.login);
Hoặc sử dụng cấu trúc promise
cơ bản:
let url = 'https://api.github.com/repos/microsoft/TypeScript/commits';
fetch(url)
.then(response => response.json())
.then(commits => alert(commits[0].author.login));
Sử dụng với .text()
thay vì .json()
:
let url = 'https://api.github.com/repos/microsoft/TypeScript/commits';
let response = await fetch(url);
let commits = await response.text(); // lấy dữ liệu trả về dưới dạng text với await
alert(text.slice(0, 80) + '...');
Trong trường hợp dữ liệu được yêu cầu là hình ảnh hoặc các tập tin, ta có thể dùng .blob()
để lấy kết quả trả về:
let response = await fetch('/article/fetch/logo-fetch.svg');
let blob = await response.blob(); // download as Blob object
// create for it
let img = document.createElement('img');
img.style = 'position:fixed;top:10px;left:10px;width:100px';
// Thêm thuộc tính src
img.src = URL.createObjectURL(blob);
document.body.append(img);
Ta chỉ có thể dùng một trong các method cho mỗi request. Nếu bạn đã lấy dữ liệu từ response.json()
thì response.text()
sẽ không hoạt động. Lý dó là vì dữ liệu đã được tải về và xử lý mới method được dùng trước đó.
let text = await response.text(); // nhận dữ liệu trả về
let parsed = await response.json(); // không hoạt động (dữ liệu đã được xử lý)
Nếu bạn cần gửi các thông số kèm theo trong GET request
, bạn có thể điều chỉnh url
theo mô hình bên dưới:
const url = "https://plnkr.co/edit/?p=preview&sidebar=app"
Response headers
Đẻ xem headers trong kết quả trả về từ máy chủ, ta dùng object response.headers
.
let response = await fetch('https://api.github.com/repos/microsoft/TypeScript/commits');
// get one header
alert(response.headers.get('Content-Type')); // application/json; charset=utf-8
// iterate over all headers
for (let [key, value] of response.headers) {
alert(`${key} = ${value}`);
}
Request headers
Để thêm các thông số vào headers
trước khi gửi đi, ta dùng tuỳ chọn headers
cho options
. Headers được đặt dưới dạng object:
let response = fetch(someUrl, {
headers: {
Authentication: 'secret'
}
});
Tuy nhiên, có một số headers ta không thể đặt khi dùng fetch()
, trình duyệt sẽ xử lý các headers này:
Accept-Charset
,Accept-Encoding
Access-Control-Request-Headers
Access-Control-Request-Method
Connection
Content-Length
Cookie
,Cookie2
Date
DNT
Expect
Host
Keep-Alive
Origin
Referer
TE
Trailer
Transfer-Encoding
Upgrade
Via
Proxy-*
Sec-*
Gửi yêu cầu với method POST
Để gửi yêu cầu dạng POST
, hoặc các dạng khác, ta sẽ dùng thêm một vài options
khác của fetch()
:
method
– HTTP-method cần dùng: POST, PUT, DELETE, ..body
– dữ liệu gửi kèm theo request, có thể là:string
: dữ liệu dạng text hoặc JSONFormData
để gửi đi dưới dạngform/multipart
Blob/BufferSource
để gửi dữ liệu nhị phân- URLSearchParams để gửi đi dưới dạng
x-www-form-urlencoded
Dữ liệu gửi kèm dưới dạng JSON được sử dụng phổ biến nhất:
let user = {
name: 'John',
surname: 'Wick'
};
let response = await fetch('/article/fetch/post/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(user)
});
// Tiếp tục xử lý kết quả trả về
Mặc định, nếu request body là có dạng string
thì header Content-Type
sẽ được đặt là text/plain;charset=UTF-8
. Nếu ta gửi body dưới dạng JSON, ta cần phải điều chỉnh header Content-Type
dưới thành application/json
để máy chủ có thể giải mã body ta gửi đi.
Gửi hình ảnh đến máy chủ
Ngoài các dạng body cơ bản ở phần trên, ta cũng có thể gửi các dạng đặc biệt khác như Blob
hoặc BufferSource
với fetch()
. Trong ví dụ bên dưới, ta dùng thẻ <canvas>
để hình ảnh, sau đó gửi hành ảnh này trong một request khi nhấp vào nút “submit”:
<body style="margin:0">
<canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>
<input type="button" value="Submit" onclick="submit()">
<script>
canvasElem.onmousemove = function(e) {
let ctx = canvasElem.getContext('2d');
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
};
async function submit() {
let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
let response = await fetch('/article/fetch/post/image', {
method: 'POST',
body: blob
});
// the server responds with confirmation and the image size
let result = await response.json();
alert(result.message);
}
</script>
</body>
Trong ví dụ trên, ta không cần đặt header Content-Type
, lý do là mỗi object dạng Blob
đều có dạng riêng biệt (ví dụ image/png
cho hình ảnh dạng png). Ta cũng có thể dùng async/await
trong ví dụ trên như sau:
function submit() {
canvasElem.toBlob(function(blob) {
fetch('/article/fetch/post/image', {
method: 'POST',
body: blob
})
.then(response => response.json())
.then(result => alert(JSON.stringify(result, null, 2)))
}, 'image/png');
}
Tạm kết
Thông thường, khi dùng fetch()
API, ta cần phải gọi hai lần await
:
let response = await fetch(url, options); // nhận kết quả trả về với response headers
let result = await response.json(); // lấy dữ liệu dạng JSON
Hoặc không dùng await
:
fetch(url, options)
.then(response => response.json())
.then(result => /* Xử lý kết quả */)
Một số thuộc tính của Response
:
response.status
– HTTP-status: 200, 404, 500response.ok
–true
nếu status từ 200 – 299response.headers
– các headers được gửi kèm theo kết quả từ máy chủ
Để lấy dữ liệu trả về, ta dùng các method sau;
response.text()
– lấy dữ liệu dưới dạng textresponse.json()
– lấy dữ liệu dưới dạng JSONresponse.formData()
– lấy dữ liệu dưới dạngFormData
response.blob()
– lấy dữ liệu dưới dạngBlob
(dữ liệu nhị phân)response.arrayBuffer()
– lấy dữ liệu dưới dạngArrayBuffer
Một số options
dùng với fetct()
:
method
– HTTP method: POST, PUT, GETheaders
– object với các headers cho yêu cầu trước khi gửi đibody
– dữ liệu gửi đến máy chủ, có dạngstring
,FormData
,BufferSource
,Blob
hoặcURLSearchParams
Tags In
Related Posts
Leave a Reply Cancel reply
Categories
- Bài viết (1)
- Blog (24)
- Code Review (3)
- Course Introduction (2)
- CSS (4)
- CSS Grid (4)
- Javascript (2)
- Lesson (11)
- Lưu dữ liệu trên trình duyệt (1)
- Network Requests (1)
- OneDirect Projects (1)
- Product Review (3)
- Stage Content (16)
- WCS Courses Content (17)