Đang lưu dữ liệu

Các cách tiết kiệm dữ liệu

ĐẶT Ghi hoặc thay thế dữ liệu bằng một đường dẫn đã xác định, chẳng hạn như fireblog/users/user1/<data>
PATCH Cập nhật một số khoá cho một đường dẫn đã xác định mà không cần thay thế tất cả dữ liệu.
BÀI ĐĂNG Thêm vào danh sách dữ liệu trong cơ sở dữ liệu Firebase của chúng ta. Mỗi khi chúng tôi gửi một yêu cầu POST, ứng dụng Firebase sẽ tạo một khoá duy nhất, chẳng hạn như fireblog/users/<unique-id>/<data>
XÓA Xoá dữ liệu khỏi thông tin tham chiếu cơ sở dữ liệu Firebase được chỉ định.

Ghi dữ liệu bằng PUT

Thao tác ghi cơ bản thông qua REST API là PUT. Để minh hoạ cách lưu dữ liệu, chúng ta sẽ tạo một ứng dụng blog có các bài đăng và người dùng. Tất cả dữ liệu cho ứng dụng của chúng ta sẽ được lưu trữ theo đường dẫn "fireblog", tại URL cơ sở dữ liệu Firebase "https://docs-examples.firebaseio.com/fireblog".

Hãy bắt đầu bằng cách lưu một số dữ liệu người dùng vào cơ sở dữ liệu Firebase của chúng ta. Chúng tôi sẽ lưu trữ từng người dùng bằng một tên người dùng riêng biệt, đồng thời lưu trữ họ tên và ngày sinh của họ. Vì mỗi người dùng sẽ có một tên người dùng riêng biệt, nên việc sử dụng PUT ở đây thay vì POST là hợp lý vì chúng ta đã có khoá và không cần tạo khoá.

Bằng cách sử dụng PUT, chúng ta có thể ghi một chuỗi, số, boolean, mảng hoặc bất kỳ đối tượng JSON nào vào cơ sở dữ liệu Firebase. Trong trường hợp này, chúng ta sẽ truyền cho nó một đối tượng:

curl -X PUT -d '{
  "alanisawesome": {
    "name": "Alan Turing",
    "birthday": "June 23, 1912"
  }
}' 'https://docs-examples.firebaseio.com/fireblog/users.json'

Khi một đối tượng JSON được lưu vào cơ sở dữ liệu, các thuộc tính đối tượng sẽ tự động được ánh xạ đến các vị trí con theo kiểu lồng nhau. Nếu chuyển đến nút mới tạo, chúng ta sẽ thấy giá trị "Alan Turing". Chúng ta cũng có thể lưu dữ liệu trực tiếp vào một vị trí con:

curl -X PUT -d '"Alan Turing"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/name.json'
curl -X PUT -d '"June 23, 1912"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/birthday.json'

Hai ví dụ trên (ghi giá trị cùng lúc với một đối tượng và ghi riêng giá trị đó vào các vị trí con) sẽ dẫn đến việc cùng một dữ liệu được lưu vào cơ sở dữ liệu Firebase của chúng tôi:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    }
  }
}

Yêu cầu thành công sẽ được biểu thị bằng mã trạng thái HTTP 200 OK và phản hồi sẽ chứa dữ liệu mà chúng ta đã ghi vào cơ sở dữ liệu. Ví dụ đầu tiên sẽ chỉ kích hoạt một sự kiện trên những ứng dụng đang theo dõi dữ liệu, trong khi ví dụ thứ hai sẽ kích hoạt hai sự kiện. Điều quan trọng cần lưu ý là nếu dữ liệu đã tồn tại ở đường dẫn người dùng, thì phương pháp đầu tiên sẽ ghi đè dữ liệu đó, nhưng phương pháp thứ hai sẽ chỉ sửa đổi giá trị của từng nút con riêng biệt trong khi giữ nguyên các nút con khác. PUT tương đương với set() trong SDK JavaScript của chúng tôi.

Cập nhật dữ liệu bằng phương thức PATCH

Bằng cách sử dụng yêu cầu PATCH, chúng ta có thể cập nhật các phần tử con cụ thể tại một vị trí mà không cần ghi đè dữ liệu hiện có. Hãy thêm biệt danh của Turing vào dữ liệu người dùng của anh ấy bằng một yêu cầu PATCH:

curl -X PATCH -d '{
  "nickname": "Alan The Machine"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

Yêu cầu trên sẽ ghi nickname vào đối tượng alanisawesome của chúng ta mà không xoá các đối tượng con name hoặc birthday. Xin lưu ý rằng nếu chúng tôi đã đưa ra yêu cầu PUT tại đây, thì namebirthday sẽ bị xoá vì không có trong yêu cầu. Dữ liệu trong cơ sở dữ liệu Firebase của chúng ta hiện có dạng như sau:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    }
  }
}

Yêu cầu thành công sẽ được biểu thị bằng mã trạng thái HTTP 200 OK và phản hồi sẽ chứa dữ liệu đã cập nhật được ghi vào cơ sở dữ liệu.

Firebase cũng hỗ trợ các bản cập nhật nhiều đường dẫn. Điều này có nghĩa là PATCH hiện có thể cập nhật các giá trị ở nhiều vị trí trong cơ sở dữ liệu Firebase của bạn cùng một lúc. Đây là một tính năng mạnh mẽ giúp bạn chuẩn hoá dữ liệu. Khi sử dụng tính năng cập nhật nhiều đường dẫn, chúng ta có thể thêm biệt hiệu cho cả Alan và Grace cùng một lúc:

curl -X PATCH -d '{
  "alanisawesome/nickname": "Alan The Machine",
  "gracehopper/nickname": "Amazing Grace"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

Sau bản cập nhật này, cả Alan và Grace đều đã được thêm biệt hiệu:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

Xin lưu ý rằng việc cố gắng cập nhật các đối tượng bằng cách ghi các đối tượng có đường dẫn được đưa vào sẽ dẫn đến hành vi khác. Hãy xem điều gì sẽ xảy ra nếu chúng ta thử cập nhật Grace và Alan theo cách này:

curl -X PATCH -d '{
  "alanisawesome": {"nickname": "Alan The Machine"},
  "gracehopper": {"nickname": "Amazing Grace"}
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

Điều này dẫn đến hành vi khác, cụ thể là ghi đè toàn bộ nút /fireblog/users:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

Cập nhật dữ liệu bằng các yêu cầu có điều kiện

Bạn có thể sử dụng các yêu cầu có điều kiện (tương đương với giao dịch trong REST) để cập nhật dữ liệu theo trạng thái hiện tại của dữ liệu đó. Ví dụ: nếu bạn muốn tăng bộ đếm lượt tán thành và muốn đảm bảo số lượt tán thành phản ánh chính xác nhiều lượt tán thành đồng thời, hãy sử dụng một yêu cầu có điều kiện để ghi giá trị mới vào bộ đếm. Thay vì hai thao tác ghi thay đổi bộ đếm thành cùng một số, một trong các yêu cầu ghi sẽ không thành công và sau đó bạn có thể thử lại yêu cầu với giá trị mới.
  1. Để thực hiện một yêu cầu có điều kiện tại một vị trí, hãy lấy giá trị nhận dạng duy nhất cho dữ liệu hiện tại tại vị trí đó hoặc ETag. Nếu dữ liệu thay đổi tại vị trí đó, ETag cũng sẽ thay đổi. Bạn có thể yêu cầu ETag bằng bất kỳ phương thức nào khác ngoài PATCH. Ví dụ sau đây sử dụng một yêu cầu GET.
    curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
    Cụ thể, việc gọi ETag trong tiêu đề sẽ trả về ETag của vị trí được chỉ định trong phản hồi HTTP.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    10 // Current value of the data at the specified location
  2. Đưa ETag được trả về vào yêu cầu PUT hoặc DELETE tiếp theo của bạn để cập nhật dữ liệu khớp cụ thể với giá trị ETag đó. Theo ví dụ của chúng ta, để cập nhật bộ đếm thành 11 (lớn hơn 1 so với giá trị ban đầu đã tìm nạp là 10) và không thực hiện yêu cầu nếu giá trị không còn khớp, hãy sử dụng mã sau:
    curl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
    Nếu giá trị của dữ liệu tại vị trí được chỉ định vẫn là 10, thì ETag trong yêu cầu PUT sẽ khớp và yêu cầu thành công, ghi 11 vào cơ sở dữ liệu.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    Cache-Control: no-cache
    
    11 // New value of the data at the specified location, written by the conditional request
    Nếu vị trí không còn khớp với ETag (điều này có thể xảy ra nếu một người dùng khác ghi giá trị mới vào cơ sở dữ liệu), thì yêu cầu sẽ không thành công mà không ghi vào vị trí. Phản hồi trả về bao gồm giá trị mới và ETag.
    HTTP/1.1 412 Precondition Failed
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    12 // New value of the data at the specified location
  3. Hãy sử dụng thông tin mới nếu bạn quyết định thử lại yêu cầu. Realtime Database không tự động thử lại các yêu cầu có điều kiện không thành công. Tuy nhiên, bạn có thể sử dụng giá trị mới và ETag để tạo một yêu cầu có điều kiện mới bằng thông tin do phản hồi thất bại trả về.

Các yêu cầu có điều kiện dựa trên REST triển khai tiêu chuẩn HTTP if-match. Tuy nhiên, các chỉ số này khác với chỉ số chuẩn ở những điểm sau:

  • Bạn chỉ có thể cung cấp một giá trị ETag cho mỗi yêu cầu if-match, chứ không thể cung cấp nhiều giá trị.
  • Mặc dù tiêu chuẩn đề xuất rằng ETag được trả về cùng với tất cả các yêu cầu, nhưng Realtime Database chỉ trả về ETag cùng với các yêu cầu bao gồm tiêu đề X-Firebase-ETag. Điều này giúp giảm chi phí thanh toán cho các yêu cầu tiêu chuẩn.

Các yêu cầu có điều kiện cũng có thể chậm hơn các yêu cầu REST thông thường.

Lưu danh sách dữ liệu

Để tạo một khoá duy nhất dựa trên dấu thời gian cho mỗi phần tử con được thêm vào một tham chiếu cơ sở dữ liệu Firebase, chúng ta có thể gửi một yêu cầu POST. Đối với đường dẫn users, chúng ta nên xác định các khoá riêng vì mỗi người dùng đều có một tên người dùng riêng biệt. Nhưng khi người dùng thêm bài đăng trên blog vào ứng dụng, chúng ta sẽ sử dụng yêu cầu POST để tự động tạo khoá cho mỗi bài đăng trên blog:

curl -X POST -d '{
  "author": "alanisawesome",
  "title": "The Turing Machine"
}' 'https://docs-examples.firebaseio.com/fireblog/posts.json'

Đường dẫn posts của chúng ta hiện có dữ liệu sau:

{
  "posts": {
    "-JSOpn9ZC54A4P4RoqVa": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

Lưu ý rằng khoá -JSOpn9ZC54A4P4RoqVa đã được tự động tạo cho chúng ta vì chúng ta đã sử dụng một yêu cầu POST. Yêu cầu thành công sẽ được biểu thị bằng mã trạng thái HTTP 200 OK và phản hồi sẽ chứa khoá của dữ liệu mới đã được thêm:

{"name":"-JSOpn9ZC54A4P4RoqVa"}

Xoá dữ liệu

Để xoá dữ liệu khỏi cơ sở dữ liệu, chúng ta có thể gửi yêu cầu DELETE kèm theo URL của đường dẫn mà chúng ta muốn xoá dữ liệu. Thao tác sau đây sẽ xoá Alan khỏi đường dẫn users của chúng tôi:

curl -X DELETE \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

Yêu cầu DELETE thành công sẽ được biểu thị bằng mã trạng thái HTTP 200 OK với một phản hồi chứa JSON null.

Tham số URI

REST API chấp nhận các tham số URI sau đây khi ghi dữ liệu vào cơ sở dữ liệu:

auth

Tham số yêu cầu auth cho phép truy cập vào dữ liệu được bảo vệ bằng Firebase Realtime Database Security Rules và được tất cả các loại yêu cầu hỗ trợ. Đối số có thể là khoá bí mật ứng dụng Firebase của chúng ta hoặc mã thông báo xác thực mà chúng ta sẽ đề cập trong phần uỷ quyền người dùng. Trong ví dụ sau, chúng ta sẽ gửi một yêu cầu POST có tham số auth, trong đó CREDENTIAL là khoá bí mật ứng dụng Firebase hoặc mã thông báo xác thực:

curl -X POST -d '{"Authenticated POST request"}' \
  'https://docs-examples.firebaseio.com/auth-example.json?auth=CREDENTIAL'

in

Tham số print cho phép chúng ta chỉ định định dạng của phản hồi từ cơ sở dữ liệu. Việc thêm print=pretty vào yêu cầu của chúng ta sẽ trả về dữ liệu ở định dạng mà con người có thể đọc được. print=pretty được hỗ trợ bởi các yêu cầu GET, PUT, POST, PATCHDELETE.

Để ngăn chặn đầu ra từ máy chủ khi ghi dữ liệu, chúng ta có thể thêm print=silent vào yêu cầu của mình. Phản hồi nhận được sẽ trống và được biểu thị bằng mã trạng thái HTTP 204 No Content nếu yêu cầu thành công. print=silent được hỗ trợ bởi các yêu cầu GET, PUT, POSTPATCH.

Ghi giá trị máy chủ

Bạn có thể ghi các giá trị trên máy chủ tại một vị trí bằng cách sử dụng giá trị phần giữ chỗ. Đây là một đối tượng có một khoá ".sv" duy nhất. Giá trị cho khoá đó là loại giá trị máy chủ mà chúng ta muốn đặt. Ví dụ: để đặt dấu thời gian khi người dùng được tạo, chúng ta có thể làm như sau:

curl -X PUT -d '{".sv": "timestamp"}' \
  'https://docs-examples.firebaseio.com/alanisawesome/createdAt.json'

"timestamp" là giá trị máy chủ duy nhất được hỗ trợ và là thời gian kể từ thời gian bắt đầu của hệ thống UNIX tính bằng mili giây.

Cải thiện hiệu suất ghi

Nếu đang ghi một lượng lớn dữ liệu vào cơ sở dữ liệu, chúng ta có thể sử dụng tham số print=silent để cải thiện hiệu suất ghi và giảm mức sử dụng băng thông. Trong hành vi ghi thông thường, máy chủ sẽ phản hồi bằng dữ liệu JSON đã được ghi. Khi print=silent được chỉ định, máy chủ sẽ ngay lập tức đóng kết nối sau khi nhận được dữ liệu, giúp giảm mức sử dụng băng thông.

Trong trường hợp chúng ta thực hiện nhiều yêu cầu đến cơ sở dữ liệu, chúng ta có thể sử dụng lại kết nối HTTPS bằng cách gửi yêu cầu Keep-Alive trong tiêu đề HTTP.

Điều kiện lỗi

REST API sẽ trả về mã lỗi trong các trường hợp sau:

Mã trạng thái HTTP
400 Yêu cầu không hợp lệ

Một trong các điều kiện lỗi sau:

  • Không thể phân tích cú pháp dữ liệu PUT hoặc POST.
  • Thiếu dữ liệu PUT hoặc POST.
  • Yêu cầu cố gắng PUT hoặc POST dữ liệu có kích thước quá lớn.
  • Lệnh gọi REST API chứa tên con không hợp lệ trong đường dẫn.
  • Đường dẫn lệnh gọi REST API quá dài.
  • Yêu cầu chứa một giá trị máy chủ không nhận dạng được.
  • Chỉ mục cho truy vấn không được xác định trong Firebase Realtime Database Security Rules.
  • Yêu cầu không hỗ trợ một trong các thông số truy vấn được chỉ định.
  • Yêu cầu kết hợp các tham số truy vấn với yêu cầu GET nông.
401 Không được phép

Một trong các điều kiện lỗi sau:

  • Mã thông báo uỷ quyền đã hết hạn.
  • Mã thông báo uỷ quyền được dùng trong yêu cầu là không hợp lệ.
  • Không xác thực được bằng access_token.
  • Yêu cầu này vi phạm Firebase Realtime Database Security Rules của bạn.
404 Không tìm thấy Không tìm thấy cơ sở dữ liệu Firebase được chỉ định.
500 Lỗi máy chủ nội bộ Máy chủ trả về một lỗi. Hãy xem thông báo lỗi để biết thêm thông tin chi tiết.
503 Không có dịch vụ Cơ sở dữ liệu theo thời gian thực của Firebase mà bạn chỉ định tạm thời không hoạt động, tức là yêu cầu không được thực hiện.

Bảo mật dữ liệu

Firebase có một ngôn ngữ bảo mật cho phép chúng ta xác định những người dùng có quyền đọc và ghi vào các nút dữ liệu khác nhau. Bạn có thể đọc thêm về vấn đề này trong Realtime Database Security Rules.

Bây giờ chúng ta đã tìm hiểu về cách lưu dữ liệu, chúng ta có thể tìm hiểu cách truy xuất dữ liệu từ cơ sở dữ liệu Firebase thông qua REST API trong phần tiếp theo.