11/08/2018, 19:58

Cùng tìm hiểu cách truy cập tài nguyên với fetch API

Để hiểu về fetch() API, chúng ta hãy cùng quay ngược dòng thời gian về những năm đầu 2000. Vào thời điểm đó các kỹ sư của Microsoft giới thiệu một khái niệm khá là mới cho các ứng dụng Web, đó là kỹ thuật AJAX. Về mặt bản chất thì kỹ thuật này xoay quanh một object có cái tên khá là độc, ...

Để hiểu về fetch() API, chúng ta hãy cùng quay ngược dòng thời gian về những năm đầu 2000. Vào thời điểm đó các kỹ sư của Microsoft giới thiệu một khái niệm khá là mới cho các ứng dụng Web, đó là kỹ thuật AJAX. Về mặt bản chất thì kỹ thuật này xoay quanh một object có cái tên khá là độc, XMLHttpRequest (tuy object này không hẳn liên quan XML). Object này giúp cho browser có thể tương tác với resource thông qua giao thức HTTP. Ít lâu sau thì W3C chuẩn hoá công nghệ này và nó được sử dụng rộng rãi như một nền tảng chủ đạo của các ứng dụng web hiện đại. Các thư viên như jQuery giúp tương tác với object này dễ dàng hơn nữa thông qua jQuery.ajax.

Trong suốt 15 năm đổ lại thì các ứng dụng web không ngừng phát triển và cải tiến. Một trong những thành tựu là sự trưởng thành của JS trên môi trường server thông qua NodeJS. Tiếc là giao thức truy suất resource thông qua HTTP rất khác giữa các implementation. Thêm vào nữa XMLHttpRequest cũng khá là phức tạp (lí do các framework tạo ra các wrapper cho nó). Đó là lí do chuẩn fetch API ra đời, nó thống nhất giao thức để để lấy resource (vd: có thể thông qua mạng).

Chuẩn này định nghĩa một lớp abstract cho các object Request, Response, Body và Status giúp lập trình viên tương tác dễ dàng với resource.

Vào thời điểm này thì bạn có thể thử nghiệm fetch() trong các browser mới, vd Chrome và Firefox. Và cũng có implementation cho NodeJS.

Lưu ý: Tôi tránh dùng từ Tài nguyên và thay vào đó dùng từ tiếng Anh resource, vì từ tiếng Việt không thoát đúng nghĩa của nó

Chúng ta hãy xem một vd sau, yêu cầu là tạo ra một AJAX call vào server. Chúng ta sẽ xem xét cách làm việc đấy với object XMLHttpRequest trước:

XMLHttpRequest

Ví dụ sau lấy được từ ra từ MDN:

function reqListener() {  
  var data = JSON.parse(this.responseText);  
  console.log(data);  
}

function reqError(err) {  
  console.log('Fetch Error :-S', err);  
}

var oReq = new XMLHttpRequest();  
oReq.onload = reqListener;  
oReq.onerror = reqError;  
oReq.open('get', './api/some.json', true);  
oReq.send();

Các bạn có thể thấy là object này yêu cầu 2 hàm chính, đó là hàm open() để tạo ra kết nối và send() để gửi request đi, thêm vào đó chúng ta có 2 hàm định nghĩa listener cho trường hợp thành công và lỗi.

Có thể thấy là ví dụ khá là phức tạp, và có kèm 2 callback.

Fetch

Cùng một ví dụ trên, với Fetch chúng ta làm như sau:

fetch('./api/some.json')  
  .then(  
    function(response) {  
      if (response.status !== 200) {  
        console.log('Looks like there was a problem. Status Code: ' +  
          response.status);  
        return;  
      }

      // Examine the text in the response  
      response.json().then(function(data) {  
        console.log(data);  
      });  
    }  
  )  
  .catch(function(err) {  
    console.log('Fetch Error :-S', err);  
  });

Có thể thấy rất rõ là hàm fetch có sử dụng then() và catch(), điều đó nói lên rằng fetch được thiết kế trên nền tảng promise giúp tránh được "địa ngục callback". Tôi sẽ không đi sâu về promise ở bài này, các bạn có thể tìm hiểu qua các tài liệu khác.

So với ví dụ của XMLHttpRequest thì có thể thấy là Fetch API rất trực quan và dễ hiểu, chúng ta sẽ cùng phân tích sâu hơn về vd ở trên.

Response

Vd ở trên, response là một Response object được trả về, chúng ta có thể truy xuất rất nhiều metadata dê dàng từ object này, vd ở trên chúng ta truy xuất status, rồi sau đó chúng ta trả về JSON. Xin lưu ý là object response này cũng là một Stream object và chúng ta có thể tiếp tục gọi json() (shortcut của JSON.parse(jsonString)) lên stream này và in JSON ra màn hình.

Thế mới thấy là object này rất là lợi hại và đc thiết kế để tận dụng các kỹ thuật lập trình JS mới. Không dừng ở đó, tôi khuyến khích các bạn tham khảo thêm API khác, vd:

response.statusText
response.type
response.url
response.headers

Các thông tin headers có thể được truy cập qua hàm get() như sau:

response.headers.get('header-name')

Thực sự là tiện lợi vô cùng :)

Tôi sẽ bàn tiếp về response.type vì đây cũng là một điểm khác biệt lớn của API này tiếp

Response Type

Khi nhận về một response, chúng sẽ được gán một trong 3 kiểu sau:

  • basic
  • cors
  • opaque

Kiểu này xác định xem resource này đến từ đâu và chúng ta nên xử lý object này ra sao.

Mặc định kiểu là basic, có nghĩa là không có giới hạn ai có thể xem response này. Nhưng nếu trường hợp nguồn gốc của request không nằm cùng nguồn của server, và server trả về response có kèm cùng CORS header, thì kiểu sẽ là cors. Từ CORS viết tắt của Crorss Origin Resources Sharing, nôm na là Chia sẻ tài nguyên không cùng nguồn gốc, bạn cần phải hiệu chỉnh server để chấp nhận request đến từ nguồn gốc lạ. Kiểu basic và kiểu core giống nhau và khác nhau ở một điểm là cors giới hạn truy cập vào headers như to Cache-Control, Content-Language, Content-Type, Expires, Last-Modified và Pragma.

Còn kiểu cuối cùng opaque là cho các response không có CORS headers đến từ server có nguồn gốc, theo spec thì response này bạn không thể truy cập bất cứ thông tin gì, và vì thế không không thể biết được response có thành công hay không, hay bạn không có quyền truy xuất resource này.

Chỉ định HTTP method khác

Mặc định fetch() dùng HTTP GET, nhưng bạn cũng có thể chỉ định method khác vd POST:

fetch(url, {  
    method: 'post',  
...

ngoài ra bạn cũng có thể chỉ định headers:

fetch(url, {
    headers: {  
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
    },  

nếu bạn gửi AJAX có dữ liệu thì có thể thông qua body:

fetch(url, {
    body: JSON.stringify({
        email: document.getElementById('email').value
        answer: document.getElementById('answer').value
    })

Tính tương thích với trình duyệt cũ

Tính đến thời điểm hiện tại thì hầu hết các browser lớn đều đã hỗ trợ hàm này, tôi có thể thấy là cả 2 Window và ServiceWorker đều đã nhúng hàm fetch vào và sẵn sàng để sử dụng. Nhưng nếu bạn phải hỗ trợ browser cũ thì có thể sử dụng polyfill fetch để chuyển đổi code để tương thích.

Sử dụng với NodeJS

Hi vọng là NodeJS sẽ hộ trợ native hàm này trong thời gian tới, hiện giờ thì có thể dùng cá gói npm như node-fetch.

Lời kết

Fetch API là một chuẩn mới giúp tạo ra giao thức thống nhất để truy xuất tài nguyền. Fetch đc thiết kế theo kiểu Promise, giúp người dùng tránh callbacks và cũng giúp xâu chuỗi promise khi xử lý. Fetch API cung cấp Request](https://fetch.spec.whatwg.org/#requests), Response, Body và Status object giúp người dùng tương tác hiệu quả với thông tin trả về. Hiện hành các trình duyệt mới đều hỗ trợ nhưng nếu bạn phải hỗ trợ các bản cũ thì có thể sử dụng các thư viện polyfill.

0