Anonystick

anonystick@gmail.com

resful api bị lỗi liên tục, bạn làm gì để làm hài lòng Client

Đây là một trường hợp rất phổ biến nhưng lại ít người quan tâm tới nó. Bởi vì họ là những lập trình viên level 1 hoặc là họ đẵ gặp rồi nhưng không có cách giải quyết. Vì sao?


Mục đích của bài viết


Ai sẽ là người nên đọc bài viết này? Tập trung chính là những lập trình viên phát triển ứng dụng hay còn gọi là web development. Và đâu đó tôi cũng mong muốn một số bạn làm nghề LTV này nên nhìn nhận lại mình, nếu không biết cách giải quyết vấn đề này thì nên chú ý bài viết này. Còn nếu bạn là một người lập trình mà bỏ qua cách khắc phục khi đã gặp tình huống thực tế rồi, thì có lẽ bạn cũng nên suy nghĩ lại chính bản thân mình có quá tệ hay không? 


Bài viết này sẽ hướng dẫn bạn cách làm thế nào để "Gọi lại API khi nó bị lỗi, và khi nào bạn không gọi nữa?" một cách thanh lịch và duyên dáng. Bài này sẽ giúp bạn học được nhiều tips hơn, về cách viết, về cách phán đoán và quan trọng làm cho những người đang sử dụng hệ thống không biết hệ thống đang bị lỗi. 


restful api error handling - Tình huống thực tế 


Nếu mà các bạn chưa biết về restful api thì có thể rất khó để đọc bài viết này. Vì bài viết này bắt buộc bạn phải hiểu về cách gọi một restful api như thế nào? Nếu chưa bạn hãy xem qua những bài viết về axios hay fetch thậm chí ajax để xem cách gọi một url thế nào? Sau đó hãy xem sự khác khau giữa GET và POST ngoài ra có nhiều method khác nữa. Nói chung là nếu bạn là người thợ mới trong nghề này thì hơi khó. Tình huống phổ biến nhất là khi gọi một request từ resful api mà back-end trả về một trạng thái bị lỗi. 


Có thể là lỗi 404, 400, hay 500. Thì lúc đó nhiều người sẽ xử lý khác nhau. Để vậy kệ, hoặc nhận thông báo lỗi ngắt không chạy lại nữa, hoặc để mặc cho web xoay vòng... Chán nhỉ. Không sao, hôm nay hay xem tôi giải quyết vụ này cho các bạn. Đây là một trong nhiều cách, nếu bạn nào biết thêm cách nào nữa thì xin góp ý nhiệt tình nhé. Chứ một thằng viết mà mấy ngàn thằng đọc nên cũng quan ngại :D kakaka


Lỗi các thông số API không hợp lệ hãy thử tải lại trang


Đầu tiên bạn phải tìm hiểu kỹ khi nào bạn nên quyết định gọi lại api một lần nữa. Trước tiên bạn phải xem xét một số HTTP Status Codes để check nếu như 404 thì bạn có nên cố gắng gọi webservice một lần nữa không? tất nhiên là không rồi. Nếu là tôi, thì tôi sẽ gọi lại api nếu HTTP Status Code trả về những code sau như: 408, 500, 502, 503, 504, 522 và 524. 


Tiếp theo bạn cũng phải cân nhắc xem nên thử lại mấy lần gọi api nếu có lỗi trên. Nếu bạn không biết thì tôi sẽ nói đó là 3 lần, vì sao ư? Vì nó được quy định ngầm trong giới giang hồ. Tiếp theo, bạn tiếp tục cân nhắc, đó là mỗi lần gọi lại có độ trễ hay không. Khái niệm này gọi là back-off. Thời gian giữa các yêu cầu sẽ tăng lên theo mỗi lần thử. Cuối cùng, chúng ta cũng cần phải quyết định có bao nhiêu lần thử trước khi từ bỏ. Và nên cân nhắc cho việc gọi là POST. Thưởng nên bỏ qua nó. Không thì trùng data là có đó. Ông nào không đọc kỹ ráng chịu nhé.


Đệ quy


Cấu trúc ở đây là như yêu cầu đệ quy vậy đó. Nó đơn giản. Đây là bài viết giải thích vì sao "Đệ quy lại đơn giản" và có một số câu hỏi đó là "khác nhau giữa đệ quy và vòng loop thế nào"? Có hết, chỉ cần click zô link là ok. Ví dụ: nếu chúng tôi muốn tiếp tục cố gắng thực hiện một yêu cầu vô hạn, nó có thể trông như thế này:


function myRequest(url, options = {}) {
  return requests(url, options, response => {
    if (response.ok) {
      return response
    } else {
      return myRequest(url, options)
    }
  })
}

Dựa trên nguyên lý trên thì chúng tôi viết lại cho nó chuẩn và phù hợp với tình hình như trên.


Add retry to Fetch api


Đầu tiên chúng ta sẽ xem xét triển khai dựa trên fetch api. Chúng ta viết lại như sau:

function fetchRetry(url, options) {
  // Return a fetch request
  return fetch(url, options).then(res => {
    // Check nếu thành công thì thôi...
    if (res.ok) return res.json()
    // Nếu sai thì gọi lại tiếp
    return fetchRetry(url, options)
  })
}

Câu hỏi ở đây, của sao nói cho phép gọi api tối đa 3 lần? Đây, từ từ đã chứ, gì mà xôm thế.

function fetchRetry(url, options = {}, retries = 3) {
  return fetch(url, options)
    .then(res => {
      if (res.ok) return res.json()

      if (retries > 0) {
        return fetchRetry(url, options, retries - 1)
      } else {
        throw new Error(res)
      }
    })
    .catch(console.error)
}

Code trên, code dưới đều giống nhau nhưng chỉ khác là chúng ta đã thêm retries = 3  vào mà thôi. Nếu nhìn vào không hiểu thì bạn là tips người nên đọc Series - Mẹo javascript. Để dùng thử, bạn có thể yêu cầu https://status-codes.glitch.me/status/400. Ví dụ:

fetchRetry("https://status-codes.glitch.me/status/400")
  .then(console.log)
  .catch(console.error)

Như vậy là đã giải được bài toán là 3 lần tối đa cho phép được gọi một api mà xuất hiện lỗi. Vậy tiếp theo câu hỏi là check những status http code nào cho hợp lệ.

function fetchRetry(url, options = {}, retries = 3) {
  const retryCodes = [408, 500, 502, 503, 504, 522, 524]
  return fetch(url, options)
    .then(res => {
      if (res.ok) return res.json()

      if (retries > 0 && retryCodes.includes(res.status)) {
        return fetchRetry(url, options, retries - 1)
      } else {
        throw new Error(res)
      }
    })
    .catch(console.error)
}

Nhìn vào chúng ta đã thêm một array = [408, 500, 502, 503, 504, 522, 524] trong đó chứa những http status code mà tôi đã khuyên các bạn nên check theo. Để tránh những trạng thái không cần thiết như 404. Điều kiện thử lại sẽ kiểm tra xem phản hồi statuscó tồn tại trong mảng bằng cách sử dụng ECMAScript array.includes() hay không. Nếu có, hãy thử yêu cầu. Nếu không, hãy thông báo lỗi. Có một tính năng cuối cùng để thêm. Độ trễ tăng giảm giữa mỗi yêu cầu. Hãy thực hiện nó.

function fetchRetry(url, options = {}, retries = 3, backoff = 300) {
  /* 1 */
  const retryCodes = [408, 500, 502, 503, 504, 522, 524]
  return fetch(url, options)
    .then(res => {
      if (res.ok) return res.json()

      if (retries > 0 && retryCodes.includes(res.status)) {
        setTimeout(() => {
          /* 2 */
          return fetchRetry(url, options, retries - 1, backoff * 2) /* 3 */
        }, backoff) /* 2 */
      } else {
        throw new Error(res)
      }
    })
    .catch(console.error)
}

Nhìn vào tiếp một lần nữa ta thấy mỗi lần thất bại thì backoff sẽ nhân đôi thời gian lên. Có nghĩa là thời gian yêu cầu api sẽ lâu hơn mỗi khi gọi lại lỗi. Ví dụ:

fetchRetry("https://status-codes.glitch.me/status/500")
  .then(console.log)
  .catch(console.error)

Lần đầu sẽ là 300ms, 600ms, sau 900ms, và lần thử cuối cùng là 1200ms. Bạn có thể thử với bất kỳ mã trạng thái nào bằng cách sử dụng https://status-codes.glitch.me/status/${STATUS_CODE}.


Tóm lại


Như vậy là xong rồi. Không nói nhiều nữa, nếu bạn thực sự là người có trách nhiệm thì hãy bắt đầu làm thôi. Đừng quên những bài viết nói về check error javascript. 
Ref: https://restfulapi.net/http-status-codes/